Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

ThreadLocal - 개발 본문

인프런/[인프런] 스프링 핵심 원리 - 고급

ThreadLocal - 개발

Seung__ 2021. 11. 22. 14:54
  • FieldLogTrace 에서 발생했던 동시성 문제를 ThreadLocal 로 해결해보자.
  • TraceId traceIdHolder 필드를 쓰레드 로컬을 사용하도록 
    ThreadLocal<TraceId> traceIdHolder로 변경
  • 필드 대신에 쓰레드 로컬을 사용해서 데이터를 동기화하는 ThreadLocalLogTrace 를 새로 만들자.

 

1. ThreadLocalLogTrace


기존 TraceId traceIdHolder를 ThreadLocal<TraceId> traceHolder로 변경하였다.

@Slf4j
public class ThreadLocalLogTrace implements LogTrace{

    private static final String START_PREFIX = "-->";
    private static final String COMPLETE_PREFIX = "<--";
    private static final String EX_PREFIX = "<X-";

    //private TraceId traceIdHolder; //traceId 동기화, 동시성 이슈 발생.
    private ThreadLocal<TraceId> traceIdHolder = new ThreadLocal<>();

    @Override
    public TraceStatus begin(String message) {
        syncTraceId();
        TraceId traceId = traceIdHolder.get();
        Long startTimeMs = System.currentTimeMillis();
        log.info("[{}] {}{}", traceId.getId(), addSpace(START_PREFIX, traceId.getLevel()), message);
        return new TraceStatus(traceId, startTimeMs, message);
    }

    private void syncTraceId(){
        TraceId traceId = traceIdHolder.get();

        if (traceId == null){
            traceIdHolder.set(new TraceId());
        } else {
            traceIdHolder.set(traceId.creatNextId());
        }
    }

    @Override
    public void end(TraceStatus status) {
        complete(status, null);
    }

    @Override
    public void exception(TraceStatus status, Exception e) {
        complete(status, e);
    }

    private void complete(TraceStatus status, Exception e){
        Long stopTimeMs = System.currentTimeMillis();
        long resultTimeMs = stopTimeMs - status.getStartTimeMs();
        TraceId traceId = status.getTraceId();

        if (e == null){
            log.info("[{}] {}{} time={}ms", traceId.getId(), addSpace(COMPLETE_PREFIX, traceId.getLevel()), status.getMessage(), resultTimeMs);
        }else {
            log.info("[{}] {}{} time={}ms ex={}", traceId.getId(), addSpace(EX_PREFIX, traceId.getLevel()), status.getMessage(), resultTimeMs, e.toString());
        }

        releaseTraceId();
    }

    private void releaseTraceId() {
        TraceId traceId = traceIdHolder.get();
        
        if (traceId.isFirstLevel()){ 
            traceIdHolder.remove();
        }else {
            traceIdHolder.set(traceId.creatPreviousId());
        }
    }

    private static String addSpace(String prefix, int level){
        StringBuilder sb = new StringBuilder();
        for (int i=0; i < level; i++){
            sb.append((i == level -1) ? "|" + prefix : "|  ");

        }

        return sb.toString();
    }
}

 

 

2. ThreadLocal.remove()


  • 추가로 쓰레드 로컬을 모두 사용하고 나면 
  • 꼭 ThreadLocal.remove() 를 호출해서 쓰레드 로컬에 저장된 값을 제거해주어야 한다.
  • 쉽게 이야기해서 다음의 마지막 로그를 출력하고 나면 쓰레드 로컬의 값을 제거해야 한다.
[3f902f0b] hello1
[3f902f0b] |-->hello2
[3f902f0b] |<--hello2 time=2ms
[3f902f0b] hello1 time=6ms //end() -> releaseTraceId() -> level==0,
ThreadLocal.remove() 호출
  • 여기서는 releaseTraceId() 를 통해 level 이 점점 낮아져서 2 1 0이 되면
  •  로그를 처음 호출한  부분으로 돌아온 것이다. 
  • 따라서 이 경우 연관된 로그 출력이 끝난 것이다. 이제 더 이상 TraceId 값을  추적하지 않아도 된다. 
  • 그래서 traceId.isFirstLevel() ( level==0 )인 경우
  • ThreadLocal.remove() 를 호출해서 쓰레드 로컬에 저장된 값을 제거해준다.

 

3. GitHub: 21112 ThreadLocal_2


 

GitHub - bsh6463/Spring_Advanced: initial

initial. Contribute to bsh6463/Spring_Advanced development by creating an account on GitHub.

github.com

 

'인프런 > [인프런] 스프링 핵심 원리 - 고급' 카테고리의 다른 글

ThreadLocal - 주의사항  (0) 2021.11.22
ThreadLocal - 적용  (0) 2021.11.22
ThreadLocal - 예제  (0) 2021.11.22
Thread Local - 소개  (0) 2021.11.22
동시성 문제 - 예제 코드  (0) 2021.11.22
Comments