ThreadLocal의 디자인 및 사용(원리편)

jdk1.2 출시 시 자바 지원.lang.ThreadLocal.J2SE5.0의 성명은:public class ThreadLocal extends ObjectThreadLocal은 무엇입니까?사실 ThreadLocal은 스레드의 로컬 구현 버전이 아니라 Thread가 아닌thread local variable (스레드 부분 변수) 입니다.아마도 ThreadLocalVar 라는 이름이 더 적합할 것입니다.
스레드 국부 변수(ThreadLocal)의 기능은 매우 간단하다. 이 변수를 사용하는 모든 스레드에 변수 값의 복사본을 제공하는 것이다. 모든 스레드는 독립적으로 자신의 복사본을 바꿀 수 있고 다른 스레드의 복사본과 충돌하지 않는다.라인의 각도에서 보면 모든 라인이 이 변수를 완전히 가지고 있는 것 같다.
먼저 ThreadLocal 클래스의 인터페이스와 디자인 방향을 살펴보겠습니다.J2SE5.0에서 이 클래스는 기본 구조 함수 1개와 일반 함수 4개가 있습니다: 보호된 ThreadLocal initial Value () 는 하위 클래스를 다시 쓰기 위해 일부러 이루어진 것입니다.이 방법은 현재 스레드가 이 스레드 국부 변수의 초기 값을 되돌려줍니다. 이 방법은 한 스레드가 get () 또는 set (Object) 을 처음 호출할 때 실행되고, 1회만 실행됩니다.public ThreadLocal get (), 현재 스레드의 로컬 변수 복사본을 되돌려줍니다.public void set(ThreadLocal value), 현재 스레드의 스레드 부분 변수 복사본의 값을 설정합니다.public void remove (), 현재 스레드의 스레드 국부 변수 복사본의 값을 제거하여 저장 공간을 방출합니다.
다음 참조를 통해 ThreadLocal의 작동 방식을 확인할 수 있습니다.

public class ThreadLocal { 
    private Map values = Collections.synchronizedMap(new HashMap());

    public Object get() {
        Thread curThread = Thread.currentThread();
        Object o = values.get(curThread);
        if (o == null && !values.containsKey(curThread)) {
            o = initialValue();
            values.put(curThread, o);
        }
        return o;
    }

    public void set(Object newValue) {
        values.put(Thread.currentThread(), newValue);
    }

    public Object initialValue() {
        return null;
    }
}

JDK에서 ThreadLocal의 실현은 전체적인 사고방식도 이와 유사하지만 이것은 공업 강도의 실현이 아니다.우선 get () 과 set () 작업은values 맵의 동기화가 필요하며, 여러 라인이 같은 ThreadLocal에 동시에 접근하면 충돌이 발생합니다.그 밖에 이 실현도 실제와 부합되지 않는다. Thread 대상으로values 맵의 키를 만들면 라인이 종료된 후에Thread에 대해 쓰레기 회수를 할 수 없을 뿐만 아니라 죽은 라인의ThreadLocal의 특정한 라인의 값에 대해서도 쓰레기 회수를 할 수 없기 때문이다.j2sdk5.0의 src를 보면 ThreadLocal에 맵이 있는 것이 아니라 모든 Thread에 이런 맵이 존재한다. 구체적으로ThreadLocal이다.ThreadLocalMap.set을 사용할 때 현재 라인의 맵에 있는 put의 키는 현재 ThreadLocal 대상입니다.현재 Thread를 Key 값으로 Thread Local의 맵에 put하는 것이 아니라
ThreadLocal 사용스레드 부분 변수가 다른 값을 초기화하려면 ThreadLocal의 하위 클래스를 직접 실현하고 이 방법을 다시 써야 한다. 보통 inner anonymous class를 사용하여 ThreadLocal에 하위 클래스를 분류한다. 예를 들어 아래의 예에서 SerialNum 클래스는 모든 클래스에 하나의 순서 번호를 분배한다.

public class SerialNum {
    // The next serial number to be assigned
    private static int nextSerialNum = 0;

    private static ThreadLocal serialNum = new ThreadLocal() {
        protected synchronized Object initialValue() {
            return new Integer(nextSerialNum++);
        }
    }

    public static int get() {
        return ((Integer) (serialNum.get())).intValue();
    }
}

스레드가 활성화되고 ThreadLocal 대상이 접근할 수 있을 때, 이 스레드는 이 스레드의 국부 변수 복사본에 대한 은밀한 인용을 가지고 있으며, 이 스레드의 운행이 끝난 후, 이 스레드가 가지고 있는 모든 스레드의 국부 변수 복사본은 효력을 상실하고 스팸 수집기가 수집하기를 기다릴 것이다.
ThreadLocal에서 어떤 종류의 대상을 보유할 수 있기 때문에 ThreadLocal을 사용하여 현재 스레드의 값을 가져오려면 강제 형식 변환이 필요합니다.하지만 J2SE5.0 모듈을 도입하면 새로운 모듈 파라미터를 지원하는 ThreadLocal 클래스가 이득을 볼 수 있습니다.또한 강제 유형 변환을 줄이고 일부 오류 검사를 컴파일 기간으로 앞당겨 ThreadLocal 사용을 어느 정도 간소화할 수 있다.
ThreadLocal은 다른 동기화 메커니즘에 비해 어떤 장점이 있습니까?ThreadLocal과 기타 모든 동기화 메커니즘은 다중 스레드에서 같은 변수에 대한 접근 충돌을 해결하기 위해 일반적인 동기화 메커니즘에서는 대상을 잠그고 여러 스레드가 같은 변수에 대한 안전한 접근을 실현한다.이때 이 변수는 여러 라인이 공유하는 것이다. 이런 동기화 메커니즘을 사용하면 언제 변수에 대해 읽기와 쓰기를 하는지, 언제 어떤 대상을 잠가야 하는지, 언제 이 대상의 자물쇠를 풀어야 하는지 세밀하게 분석해야 한다.이 모든 것은 여러 라인이 자원을 공유했기 때문이다.ThreadLocal은 다른 각도에서 다중 스레드의 병렬 접근을 해결한다. ThreadLocal은 모든 스레드에 이 스레드와 연결된 변수의 복사본을 유지하여 여러 스레드의 데이터를 격리하고 모든 스레드는 자신의 변수 복사본을 가지기 때문에 이 변수를 동기화할 필요가 없다.ThreadLocal은 다중 루틴 코드를 작성할 때 안전하지 않은 모든 변수를 ThreadLocal에 봉인하거나 그 대상의 특정한 루틴 상태를 ThreadLocal에 봉인할 수 있는 안전한 공유 대상을 제공합니다.
물론 ThreadLocal은 동기화 메커니즘을 대체할 수 없으며, 두 가지가 향하는 문제 영역은 다르다.동기화 메커니즘은 여러 개의 라인이 같은 자원에 대한 병렬 접근을 동기화하기 위한 것이고 여러 개의 라인 간에 통신을 하는 효과적인 방식이다.ThreadLocal은 여러 라인을 격리하는 데이터 공유로 근본적으로 여러 라인 사이에서 자원(변수)을 공유하지 않기 때문에 여러 라인을 동기화할 필요가 없다.따라서 여러 개의 라인 간의 통신이 필요하다면 동기화 메커니즘을 사용한다.만약 여러 개의 루트 간의 공유 충돌을 격리해야 한다면 ThreadLocal을 사용할 수 있습니다. 이것은 우리의 프로그램을 크게 간소화하고 프로그램을 더욱 읽기 쉽고 간결하게 할 것입니다.ThreadLocal 클래스는 각 스레드에 로컬 변수를 저장할 수 있는 장소를 제공합니다.
본질적으로 현재 실행 중인 Thread마다 맵이 있습니다. ThreadLocal 클래스는 이 맵에 대한 접근을 봉인합니다. 따라서 라인에서 새로 생성된 대상을 ThreadLocal을 통해 이 맵에 넣을 수 있습니다. 이로써 이 라인이 앞으로 ThreadLocal 대상 즉 이 맵에서 얻은 대상은 이 라인에서만 사용할 수 있고 다른 라인에 접근하지 않을 수 있습니다.
글에서 ThreadLocal의 디자인과 사용에 대해 언급한 바와 같이 다음과 같은 실현은 사실 옳지 않다.

public class ThreadLocal
{
 private Map values = Collections.synchronizedMap(new HashMap());
 public Object get()
 {
  Thread curThread = Thread.currentThread();
  Object o = values.get(curThread);
  if (o == null && !values.containsKey(curThread))
  {
   o = initialValue();
   values.put(curThread, o);
  }
  return o;
 }

 public void set(Object newValue)
 {
  values.put(Thread.currentThread(), newValue);
 }

 public Object initialValue()
 {
  return null;
 }
}


실제로 ThreadLocal이 이 맵을 유지하는 것이 아니라 모든 Thread가 이 맵을 유지하는 것이다.이렇게 하면 매번 라인이 죽을 때마다 모든 맵에서 인용된 대상은 이 Thread의 죽음에 따라 쓰레기 수집기와 함께 수집된다. (물론 전제는 다른 곳에서 인용되지 않는다는 것이다)
이 맵의 키는 ThreadLocal 대상에 대한 약한 인용입니다. ThreadLocal 대상을 버릴 때 스팸 수집기는 이 키의 인용을 무시하고 ThreadLocal 대상을 삭제합니다.
Java Doc의 제안에 따라 ThreadLocal은 일반적으로 public static로 선언됩니다.
메소드 요약
T get()
이 스레드 국부 변수의 현재 스레드 복사본의 값을 되돌려줍니다.
protected    T initialValue()
이 스레드의 국부 변수의 현재 스레드의 '초기값' 을 되돌려줍니다.
void remove()
이 스레드의 국부 변수 현재 스레드의 값을 제거합니다.
void set(T value)
이 스레드 국부 변수의 현재 스레드 복사본의 값을 지정한 값으로 설정합니다.

좋은 웹페이지 즐겨찾기