목차
동시성 이슈 발생
여러 Thread가 동시에 Application 로직을 호출하게 되면서 Level 상태값이 꼬여서 보이게 된다.
Thread 로 구분해서 로그 확인
2021-12-06 02:18:26.028 INFO 22044 --- [nio-8080-exec-1] c.e.a.trace.logtrace.FieldLogTrace : [c0f5fc0a] OrderController.request() 2021-12-06 02:18:26.029 INFO 22044 --- [nio-8080-exec-1] c.e.a.trace.logtrace.FieldLogTrace : [c0f5fc0a] |-->OrderService.orderItem() 2021-12-06 02:18:26.029 INFO 22044 --- [nio-8080-exec-1] c.e.a.trace.logtrace.FieldLogTrace : [c0f5fc0a] | |-->OrderRepository.save() 2021-12-06 02:18:27.033 INFO 22044 --- [nio-8080-exec-1] c.e.a.trace.logtrace.FieldLogTrace : [c0f5fc0a] | |<--OrderRepository.save() time=1004ms 2021-12-06 02:18:27.033 INFO 22044 --- [nio-8080-exec-1] c.e.a.trace.logtrace.FieldLogTrace : [c0f5fc0a] |<--OrderService.orderItem() time=1004ms 2021-12-06 02:18:27.033 INFO 22044 --- [nio-8080-exec-1] c.e.a.trace.logtrace.FieldLogTrace : [c0f5fc0a] OrderController.request() time=1005ms
|
2021-12-06 02:18:26.114 INFO 22044 --- [nio-8080-exec-2] c.e.a.trace.logtrace.FieldLogTrace : [c0f5fc0a] | | |-->OrderController.request() 2021-12-06 02:18:26.115 INFO 22044 --- [nio-8080-exec-2] c.e.a.trace.logtrace.FieldLogTrace : [c0f5fc0a] | | | |-->OrderService.orderItem() 2021-12-06 02:18:26.115 INFO 22044 --- [nio-8080-exec-2] c.e.a.trace.logtrace.FieldLogTrace : [c0f5fc0a] | | | | |-->OrderRepository.save() 2021-12-06 02:18:27.119 INFO 22044 --- [nio-8080-exec-2] c.e.a.trace.logtrace.FieldLogTrace : [c0f5fc0a] | | | | |<--OrderRepository.save() time=1004ms 2021-12-06 02:18:27.120 INFO 22044 --- [nio-8080-exec-2] c.e.a.trace.logtrace.FieldLogTrace : [c0f5fc0a] | | | |<--OrderService.orderItem() time=1005ms 2021-12-06 02:18:27.120 INFO 22044 --- [nio-8080-exec-2] c.e.a.trace.logtrace.FieldLogTrace : [c0f5fc0a] | | |<--OrderController.request() time=1006ms
|
Thread 를 이용한 테스트 코드 작성
@Slf4j public class FieldService {
private String nameStore;
public String logic(String name){ log.info("저장 name={} -> nameStore = {}", name, nameStore); nameStore = name; sleep(1000); log.info("조회 nameStore = {}", nameStore); return nameStore; }
private void sleep(int millis) { try { Thread.sleep(millis); }catch (InterruptedException e){ e.printStackTrace(); } } }
|
@Slf4j public class FieldServiceTest {
private FieldService fieldService = new FieldService();
@Test public void field(){ log.info("main start"); Runnable userA = () -> { fieldService.logic("userA"); };
Runnable userB = () -> { fieldService.logic("userA"); };
Thread threadA = new Thread(userA); threadA.setName("thread - A"); Thread threadB = new Thread(userB); threadB.setName("thread - B");
threadA.start(); sleep(2000); threadB.start(); }
private void sleep(int millis){ try { Thread.sleep(millis); }catch (Exception e){ e.printStackTrace(); } } }
|
14:10:58.072 [Test worker] INFO com.example.advancedspring.trace.threadlocal.FieldServiceTest - main start 14:10:58.076 [thread - A] INFO com.example.advancedspring.trace.threadlocal.code.FieldService - 저장 name=userA -> nameStore = null 14:10:59.081 [thread - A] INFO com.example.advancedspring.trace.threadlocal.code.FieldService - 조회 nameStore = userA 14:11:00.078 [thread - B] INFO com.example.advancedspring.trace.threadlocal.code.FieldService - 저장 name=userB -> nameStore = userA 14:11:01.080 [thread - B] INFO com.example.advancedspring.trace.threadlocal.code.FieldService - 조회 nameStore = userB
|
동시성 이슈가 발생하는 코드
@Test public void field(){ log.info("main start"); Runnable userA = () -> { fieldService.logic("userA"); };
Runnable userB = () -> { fieldService.logic("userB"); };
Thread threadA = new Thread(userA); threadA.setName("thread - A"); Thread threadB = new Thread(userB); threadB.setName("thread - B");
threadA.start(); sleep(100); threadB.start();
sleep(2000); }
|
14:32:12.377 [Test worker] INFO com.example.advancedspring.trace.threadlocal.FieldServiceTest - main start 14:32:12.381 [thread - A] INFO com.example.advancedspring.trace.threadlocal.code.FieldService - 저장 name=userA -> nameStore = null 14:32:12.483 [thread - B] INFO com.example.advancedspring.trace.threadlocal.code.FieldService - 저장 name=userB -> nameStore = userA 14:32:13.387 [thread - A] INFO com.example.advancedspring.trace.threadlocal.code.FieldService - 조회 nameStore = userB 14:32:13.489 [thread - B] INFO com.example.advancedspring.trace.threadlocal.code.FieldService - 조회 nameStore = userB
|
동시성 문제는 지역 변수에서는 발생하지 않는다. 지역 변수는 쓰레드마다 각각 다른 메모리 영역에 할당된다.
동시성 문제가 발생하는 곳은 같은 인스턴스의 필드 (주로 싱글톤에서 발생) 또는 static 같은 공용 필드에서 발생한다.