일반적인 Thread 사용시 여러 사용자가 동시 요청하면 동시성 문제가 생길 수 있다. 동시성 문제가 발생하면 꽤나 골치아프게 됨.. 특히 스프링은 Singleton 방식이기 때문에 치명적이다.
실제로 실습중 요청을 여러번 보냈더니 아래와 같이 로그가 수상하게 찍힘.
2022-05-30 13:17:52.687 INFO 46437 --- [nio-8080-exec-4] s.datajpa.trace.logtrace.FieldLogTrace : [9051a058] OrderController.request()
2022-05-30 13:17:52.687 INFO 46437 --- [nio-8080-exec-4] s.datajpa.trace.logtrace.FieldLogTrace : [9051a058] |-->OrderService.orderItem()
2022-05-30 13:17:52.687 INFO 46437 --- [nio-8080-exec-4] s.datajpa.trace.logtrace.FieldLogTrace : [9051a058] | |-->OrderRepository.save()
2022-05-30 13:17:53.195 INFO 46437 --- [nio-8080-exec-5] s.datajpa.trace.logtrace.FieldLogTrace : [9051a058] | | |-->OrderController.request()
2022-05-30 13:17:53.196 INFO 46437 --- [nio-8080-exec-5] s.datajpa.trace.logtrace.FieldLogTrace : [9051a058] | | | |-->OrderService.orderItem()
2022-05-30 13:17:53.196 INFO 46437 --- [nio-8080-exec-5] s.datajpa.trace.logtrace.FieldLogTrace : [9051a058] | | | | |-->OrderRepository.save()
2022-05-30 13:17:53.692 INFO 46437 --- [nio-8080-exec-4] s.datajpa.trace.logtrace.FieldLogTrace : [9051a058] | |<--OrderRepository.save() time=1005ms
2022-05-30 13:17:53.693 INFO 46437 --- [nio-8080-exec-4] s.datajpa.trace.logtrace.FieldLogTrace : [9051a058] |<--OrderService.orderItem() time=1006ms
2022-05-30 13:17:53.693 INFO 46437 --- [nio-8080-exec-4] s.datajpa.trace.logtrace.FieldLogTrace : [9051a058] OrderController.request() time=1006ms
2022-05-30 13:17:54.200 INFO 46437 --- [nio-8080-exec-5] s.datajpa.trace.logtrace.FieldLogTrace : [9051a058] | | | | |<--OrderRepository.save() time=1004ms
2022-05-30 13:17:54.201 INFO 46437 --- [nio-8080-exec-5] s.datajpa.trace.logtrace.FieldLogTrace : [9051a058] | | | |<--OrderService.orderItem() time=1005ms
2022-05-30 13:17:54.201 INFO 46437 --- [nio-8080-exec-5] s.datajpa.trace.logtrace.FieldLogTrace : [9051a058] | | |<--OrderController.request() time=1006ms
스프링에서 동시성을 해결하는 방법은 무엇일까?
답은 바로 ThreadLocal을 사용하는 것!
ThreadLocal은 각각의 지정공간에 값 세팅이 가능하다. 따라서 소유자가 구별된다는 소리. 즉 동시성 문제에서 자유롭게 된다.
2022-05-30 13:47:07.109 INFO 67935 --- [nio-8080-exec-1] s.d.trace.logtrace.ThreadLocalLogTrace : [165eb75e] OrderController.request()
2022-05-30 13:47:07.109 INFO 67935 --- [nio-8080-exec-1] s.d.trace.logtrace.ThreadLocalLogTrace : [165eb75e] |-->OrderService.orderItem()
2022-05-30 13:47:07.113 INFO 67935 --- [nio-8080-exec-1] s.d.trace.logtrace.ThreadLocalLogTrace : [165eb75e] | |-->OrderRepository.save()
2022-05-30 13:47:07.746 INFO 67935 --- [nio-8080-exec-3] s.d.trace.logtrace.ThreadLocalLogTrace : [60f862c2] OrderController.request()
2022-05-30 13:47:07.746 INFO 67935 --- [nio-8080-exec-3] s.d.trace.logtrace.ThreadLocalLogTrace : [60f862c2] |-->OrderService.orderItem()
2022-05-30 13:47:07.747 INFO 67935 --- [nio-8080-exec-3] s.d.trace.logtrace.ThreadLocalLogTrace : [60f862c2] | |-->OrderRepository.save()
2022-05-30 13:47:08.114 INFO 67935 --- [nio-8080-exec-1] s.d.trace.logtrace.ThreadLocalLogTrace : [165eb75e] | |<--OrderRepository.save() time=1000ms
2022-05-30 13:47:08.114 INFO 67935 --- [nio-8080-exec-1] s.d.trace.logtrace.ThreadLocalLogTrace : [165eb75e] |<--OrderService.orderItem() time=1005ms
2022-05-30 13:47:08.114 INFO 67935 --- [nio-8080-exec-1] s.d.trace.logtrace.ThreadLocalLogTrace : [165eb75e] OrderController.request() time=1005ms
2022-05-30 13:47:08.747 INFO 67935 --- [nio-8080-exec-3] s.d.trace.logtrace.ThreadLocalLogTrace : [60f862c2] | |<--OrderRepository.save() time=1000ms
2022-05-30 13:47:08.749 INFO 67935 --- [nio-8080-exec-3] s.d.trace.logtrace.ThreadLocalLogTrace : [60f862c2] |<--OrderService.orderItem() time=1003ms
2022-05-30 13:47:08.749 INFO 67935 --- [nio-8080-exec-3] s.d.trace.logtrace.ThreadLocalLogTrace : [60f862c2] OrderController.request() time=1003ms
Thread Id도 구분되고 정상적으로 로그가 출력됨.
ThreadLocal<T> threadLocal = new ThreadLocal<>();
T value;
threadLocal.set(value);
threadLocal.get();
threadLocal.remove();