개발언어/JAVA

동시성 문제 해결(ThreadLocal)

nomoreFt 2022. 8. 31. 23:48

동시성 문제 발생 예시

주로 싱글톤에서 static Field / 인스턴스 Field 등 작업단위에서 공용으로
변수를 접근하여 수정할 경우 동시성 문제가 발생한다.

private int id;//인스턴스 변수

public int logic(int uniqueId) {
        log.info("저장 id={} -> uniqueId={}", id,uniqueId);
        id = uniqueId;
        sleep(1000);
        log.info("조회 uniqueId={}", id);
        return id;
        }

  • logic 이란 메서드를 동시에 Thread가 접근하여 실행하면,(여러 사용자가 동시에 사용)

정상 동작

ThreadA -> id 3으로 저장
ThreadA -> id = 3 출력
ThreadB -> id 4로 저장
ThreadB -> id = 4 출력
3, 4 출력


동시에 접근시 문제

ThreadA -> id 3으로 저장
ThreadB -> id 4로 저장
ThreadA -> id 4로 출력 (같은 저장소 변수)
ThreadB -> id 4로 출력

4가 2번 출력된다.


해결법 - ThreadLocal

ThreadLocal : 쓰레드를 구분하여 쓰레드 별 변수 필드 저장을 하는 것 (동시에 같은 변수를 건드리지 않는다)

  • 각 쓰레드마다 별도의 내부 저장소를 사용하기 때문.
  • ThreadLocal은 thread 전용 보관소 허브같은 역할 (A - A의 보관소)

사용 예시

동시성 문제가 있을 변수에 ThreadLocal로 쓰레드 별, 각각 값을 저장하게 한다.

    private ThreadLocal<Long> threadLocalId = new ThreadLocal<>();

threadLocalId.set(3 or 4)//threadA는 3으로 저장, threadB는 4로 저장
threadLocalId.get();//threadA는 자신의 저장소에서 3을 꺼내고, B는 4를 꺼낸다.
threadLocalId.remove();//사용이 끝나면 꼭 remove()

주의사항

해당 쓰레드가 쓰레드 로컬 저장소를 모두 사용하게 되면, ThreadLocal.remove()를 사용해서 저장된 값을 해제해야 한다.

그렇지 않을 시, WAS(톰캣) 처럼 쓰레드 풀을 사용하는 경우에는 Danger 문제 발생한다.

why?

  • WAS는 절약을 위해 쓰레드 풀에 쓰레드를 미리 여러개 생성해 놓고, 사용 한 뒤 반환하는 재사용방식으로 사용하는데,
    ThreadLocal에 데이터를 remove 하지 않았다면, 해당 쓰레드의 값이 유지가 되어서 다른 요청임에도
    이전 사용된 값이 그대로 호출되어 사용된다.

꼭 사용이 끝나면 ThreadLocal remove()를 사용해서 사용이 끝나면 지워줘야한다.