스프링/스프링 핵심 원리 - 고급편 36

16. 쓰레드로컬 주의사항

쓰레드 로컬 데이터가 남아있는 이유는, 쓰레드는 생성하는 비용이 비싸기 때문에 미리 만들어 놨다가 풀링을 한다. 그래서, 해당 쓰레드가 종료된게 아니기 때문에 쓰레드로컬의 데이터 또한 남아있다(보통 초기화 작업을 해 놓지 않나? 안해놓나 보네.) 그래서, 스프링의 필터나 인터셉터 등을 이용하던지, 아니면 최소한 저렇게 로직에서 remove()를 해 줘야 한다.

14. 쓰레드로컬 예시

비슷하다. 기존에서, @Slf4j public class ThreadLocalService { private ThreadLocal nameStore = new ThreadLocal(); public String logic(String name){ log.info("저장 name={} -> nameStore={}", name, nameStore); nameStore.set(name); sleep(1000); log.info("조회 nameStore={}", nameStore.get()); return nameStore.get(); } private void sleep(int millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { t..

12. 동시성 문제 예시

한 필드에 대해 처리하고 있는데, 중간에 다른 쓰레드가 같은 필드에 대해 처리하게 되면, 둘이 처리가 겹쳐서 결과적으로 원하는 결과가 나오지 않게 된다. 테스트 @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); } ca..

11. 동시성 문제

@Configuration public class LogTraceConfig { @Bean public LogTrace logTrace(){ return new FieldLogTrace(); } } 이렇게 수동으로 빈으로 등록했다. @Bean하면 저 로직이 필요한 시간에 잘 호출되어 빈으로 등록이 된다. 즉, Bean의 기본 설정인 싱글톤으로 등록되었는데, 이제 막 새로고침 하면서 여러번 해 보니까, 이런 식으로 싱글톤인 하나의 필드, 즉 traceId에 참조한다.

10. 필드 동기화 적용

그냥 뭐 비슷함 @GetMapping("/v3/request") public String request(@RequestParam("itemId") String itemId){ TraceStatus status = null; try{ status = trace.begin("OrderController.request()"); orderService.orderItem(itemId); trace.end(status); return "ok"; } catch (Exception e){ trace.exception(status, e); throw e; } } begin, 컨트롤러에서 처음 호출 하는 거니 new 해서 새로 holder에 들어갈 거고, 서비스 public void orderItem(String item..

9. 필드 동기화

그냥 빈으로 (즉 디폴트는 싱글톤으로) 등록하고, 필드에 가지고 있게끔 해서 동기화 시키는 거다. 근데 이건 동시성 이슈가 있다. 싱글톤이기에 공유가 된다. public interface LogTrace { TraceStatus begin(String message); void end(TraceStatus status); void exception(TraceStatus status, Exception e); } 일단 인터페이스로 만든다. 갈아끼울 것이다. @Slf4j public class FieldLogTrace implements LogTrace{ private static final String START_PREFIX = "-->"; private static final String COMPLETE..

7. V2 적용

컨트롤러 @GetMapping("/v2/request") public String request(@RequestParam("itemId") String itemId){ TraceStatus status = null; try{ status = trace.begin("OrderController.request()"); orderService.orderItem(status.getTraceId(),itemId); trace.end(status); return "ok"; } catch (Exception e){ trace.exception(status, e); throw e; } } 이제 다음으로 호출하는 메소드에, 서비스에 traceId를 넘겨준다. 서비스 public void orderItem(TraceId ..