자바 병렬 프로 그래 밍 의 ThreadLocal 상세 설명 및 인 스 턴 스

Java ThreadLocal 이해
요약:
ThreadLocal 은 스 레 드 부분 변수 라 고도 부 르 는데 자바 에서 비교적 특수 한 스 레 드 바 인 딩 체제 로 변수 가 서로 다른 스 레 드 간 의 격 리 성 을 확보 하여 모든 스 레 드 가 자신의 상 태 를 처리 하 는 데 편리 하도록 합 니 다.더 나 아가 본 고 는 ThreadLocal 류 의 소스 코드 를 착안점 으로 삼 아 ThreadLocal 류 의 역할 원 리 를 깊이 분석 하고 응용 장면 과 일반 사용 절 차 를 제시 했다.
ThreadLocal 에 대한 이해
1).ThreadLocal 개요
ThreadLocal 은 스 레 드 부분 변수 라 고도 부 르 는데 자바 에서 비교적 특수 한 스 레 드 바 인 딩 체제 로 이 변 수 를 사용 하 는 스 레 드 에 변수 값 의 사본 을 제공 할 수 있 으 며 모든 스 레 드 는 자신의 사본 을 독립 적 으로 바 꿀 수 있 으 며 다른 라인 의 사본 과 충돌 하지 않 습 니 다.ThreadLocal 을 통 해 액세스 하 는 데 이 터 는 항상 현재 스 레 드 와 관련 이 있 습 니 다.즉,JVM 은 모든 실행 스 레 드 에 개인 적 인 로 컬 인 스 턴 스 액세스 공간 을 연결 하여 다 중 스 레 드 환경 에서 자주 발생 하 는 동시 방문 문제 에 격 리 체 제 를 제공 합 니 다.
2).ThreadLocal 의 JDK 정의
ThreadLocal
이 클래스 는 스 레 드-로 컬 변 수 를 제공 합 니 다.이 변 수 는(get 또는 set 메 서 드 를 통 해)하나 에 액세스 하 는 각 스 레 드 에 자체 적 으로 액세스 하 는 일반 대응(사본)과 다 릅 니 다. independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).
각 스 레 드 는 스 레 드 가 살아 있 고 ThreadLocal 인 스 턴 스 가 액세스 할 수 있 는 한 스 레 드-로 컬 변수의 복사 본 에 대한 암시 적 참 조 를 보유 합 니 다.after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).
       
우 리 는 그 중에서 세 가지 요점 을 뽑 을 수 있다.
모든 스 레 드 에는 이 ThreadLocal 변수 에 대한 개인 값 이 있 습 니 다.
모든 스 레 드 는 다른 스 레 드 에 독립 된 컨 텍스트 로 이 변수의 값 을 저장 하고 다른 스 레 드 는 보이 지 않 습 니 다.
변수의 초기 값 에 독립
ThreadLocal 은 초기 값 을 지정 할 수 있 습 니 다.그러면 모든 스 레 드 는 이 초기 화 값 의 복사 본 을 얻 을 수 있 고 모든 스 레 드 가 이 값 에 대한 수정 은 다른 스 레 드 에 보이 지 않 습 니 다.
상태 가 어떤 스 레 드 와 연결 되 어 있 습 니 다.
ThreadLocal 은 공유 변수의 문 제 를 해결 하 는 데 사용 되 는 것 이 아니 라 스 레 드 동기 화 를 조율 하기 위해 존재 하 는 것 이 아니 라 모든 스 레 드 가 자신의 상 태 를 처리 하 는 데 편리 하도록 도 입 된 메커니즘 입 니 다.이 점 을 이해 하 는 것 이 ThreadLocal 을 정확하게 사용 하 는 데 중요 합 니 다.
3).응용 장면
클래스 ThreadLocal 은 모든 스 레 드 에 자신의 값 을 연결 하여 자신의 상 태 를 처리 하 는 데 편리 하도록 하 는 것 을 해결 합 니 다.구체 적 으로 말 하면 ThreadLocal 변 수 를 전역 적 으로 데 이 터 를 저장 하 는 상자 에 비유 할 수 있 고 상자 에 모든 스 레 드 의 개인 데 이 터 를 저장 할 수 있 습 니 다.예 를 들 어 다음 종 류 는 모든 스 레 드 에 대한 유일한 부분 식별 자 를 만 드 는 데 사용 된다.스 레 드 ID 는 유 니 크 Num.get()을 처음 호출 할 때 분 배 됩 니 다.후속 호출 에 서 는 변경 되 지 않 습 니 다.

import java.util.concurrent.atomic.AtomicInteger;

public class UniqueThreadIdGenerator {
  private static final AtomicInteger uniqueId = new AtomicInteger(0);

  private static final ThreadLocal<Integer> uniqueNum = new ThreadLocal<Integer>() {
    @Override
    protected Integer initialValue() {
      return uniqueId.getAndIncrement();
    }
  };

  public static void main(String[] args) {
    Thread[] threads = new Thread[5];
    for (int i = 0; i < 5; i++) {
      String name = "Thread-" + i;
      threads[i] = new Thread(name){
        @Override
        public void run() {
          System.out.println(Thread.currentThread().getName() + ": "
              + uniqueNum.get());
        }
      };
      threads[i].start();
    }

    System.out.println(Thread.currentThread().getName() + ": "
        + uniqueNum.get());
  }
}/* Output(       ): 
    Thread-1: 2
    Thread-0: 0
    Thread-2: 3
    main: 1
    Thread-3: 4
    Thread-4: 5
 *///:~

2.ThreadLocal 류 깊이 분석
다음은 ThreadLocal 의 구체 적 인 실현 을 살 펴 보 겠 습 니 다.이 유형 은 모두 네 가지 방법 을 제공 합 니 다.

public T get() { }
public void set(T value) { }
public void remove() { }
protected T initialValue() { }
그 중에서 get()방법 은 ThreadLocal 변수 가 현재 스 레 드 에 저 장 된 값 을 가 져 오 는 것 입 니 다.set()는 ThreadLocal 변수 가 현재 스 레 드 에 있 는 값 을 설정 하 는 데 사 용 됩 니 다.reove()는 현재 스 레 드 와 관련 된 ThreadLocal 변 수 를 제거 하 는 데 사 용 됩 니 다.initialValue()는 proctected 방법 으로 재 작성 이 필요 합 니 다.
1.원리 탐구
1).착안점:get()
우선,우 리 는 먼저 그 소스 코드 를 본다.

 /**
   * Returns the value in the current thread's copy of this
   * thread-local variable. If the variable has no value for the
   * current thread, it is first initialized to the value returned
   * by an invocation of the {@link #initialValue} method.
   *
   * @return the current thread's value of this thread-local
   */
  public T get() {
    Thread t = Thread.currentThread();  //         
    ThreadLocalMap map = getMap(t);   //             threadLocals
    if (map != null) {
      //        ThreadLocalMap     thread-local variable     entry
      ThreadLocalMap.Entry e = map.getEntry(this);  
      if (e != null)   
        return (T)e.value;  //      
    }
    return setInitialValue(); 
  }
2).관건:setInitialValue()

/**
   * Variant of set() to establish initialValue. Used instead
   * of set() in case user has overridden the set() method.
   *
   * @return the initial value
   */
  private T setInitialValue() {
    T value = initialValue();   //        null
    Thread t = Thread.currentThread();  //       
    ThreadLocalMap map = getMap(t);   //        ThreadLocalMap    threadLocals
    if (map != null)
      map.set(this, value); //   map       ThreadLocal   
    else
      createMap(t, value);  
    return value;
  }
우 리 는 이 어 상기 방법 과 관련 된 세 가지 방법 을 살 펴 보 았 다.initial Value(),set(this,value)와 createMap(t,value)이다.
(1) initialValue()

 /**
   * Returns the current thread's "initial value" for this
   * thread-local variable. This method will be invoked the first
   * time a thread accesses the variable with the {@link #get}
   * method, unless the thread previously invoked the {@link #set}
   * method, in which case the <tt>initialValue</tt> method will not
   * be invoked for the thread. Normally, this method is invoked at
   * most once per thread, but it may be invoked again in case of
   * subsequent invocations of {@link #remove} followed by {@link #get}.
   *
   * <p>This implementation simply returns <tt>null</tt>; if the
   * programmer desires thread-local variables to have an initial
   * value other than <tt>null</tt>, <tt>ThreadLocal</tt> must be
   * subclassed, and this method overridden. Typically, an
   * anonymous inner class will be used.
   *
   * @return the initial value for this thread-local
   */
  protected T initialValue() {
    return null;      //        null
  }
(2) createMap()

/**
   * Create the map associated with a ThreadLocal. Overridden in
   * InheritableThreadLocal.
   *
   * @param t the current thread
   * @param firstValue value for the initial entry of the map
   * @param map the map to store.
   */
  void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue); // this      ThreadLocal   ,  map    
  }
이로써 대부분의 친구 들 은 ThreadLocal 클래스 가 각 라인 에 변 수 를 만 드 는 복사 본 을 어떻게 만 드 는 지 알 게 되 었 을 것 입 니 다.
① 모든 스 레 드 Thread 내부 에 ThreadLocal.ThreadLocalMap 형식의 구성원 변수 threadLocals 가 있 습 니 다.이 threadLocals 는 실제 ThreadLocal 변수 사본 을 저장 하 는 데 사 용 됩 니 다.키 값 은 현재 ThreadLocal 변수 이 고 value 는 변수의 사본(값)입 니 다.
② 처음에 Thread 에 서 는 threadLocals 가 비어 있 습 니 다.ThreadLocal 변 수 를 통 해 get()방법 이나 set()방법 을 호출 하면 Thread 클래스 의 threadLocals 를 초기 화하 고 현재 ThreadLocal 변 수 를 키 로 하여 ThreadLocal 에 저장 할 값 을 value 로 하여 threadLocals 에 저장 합 니 다.
③ 그리고 현재 스 레 드 에서 던 전 변 수 를 사용 하려 면 get 방법 으로 해당 스 레 드 의 thread Locals 에서 찾 을 수 있 습 니 다.
2.사례 검증
다음은 하나의 예 를 통 해 ThreadLocal 을 통 해 모든 스 레 드 에서 변수 던 전 을 만 드 는 효 과 를 증명 합 니 다.

public class Test {

  ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
  ThreadLocal<String> stringLocal = new ThreadLocal<String>();

  public void set() {
    longLocal.set(Thread.currentThread().getId());
    stringLocal.set(Thread.currentThread().getName());
  }

  public long getLong() {
    return longLocal.get();
  }

  public String getString() {
    return stringLocal.get();
  }

  public static void main(String[] args) throws InterruptedException {
    final Test test = new Test();

    test.set();
    System.out.println("    main :");
    System.out.println(test.getLong());
    System.out.println(test.getString());

    Thread thread1 = new Thread() {
      public void run() {
        test.set();
        System.out.println("
Thread-0 :"); System.out.println(test.getLong()); System.out.println(test.getString()); }; }; thread1.start(); } }/* Output: main : 1 main Thread-0 : 12 Thread-0 *///:~
이 코드 의 출력 결 과 를 통 해 알 수 있 듯 이 main 스 레 드 와 thread 1 스 레 드 에서 longLocal 에 저 장 된 던 전 값 과 stringLocal 에 저 장 된 던 전 값 이 다 르 고 더 나 아가 알 수 있 습 니 다.
  • 실제로 ThreadLocal 을 통 해 만 든 복사 본 은 모든 스 레 드 자신의 thread Locals 에 저 장 됩 니 다
  • 왜 threadLocals 의 유형 ThreadLocalMap 의 키 값 은 ThreadLocal 대상 입 니까?모든 스 레 드 에 여러 개의 threadLocal 변수 가 있 기 때 문 입 니 다.위 코드 의 longLocal 과 stringLocal 과 같 습 니 다
  • get 을 진행 하기 전에 반드시 set 를 해 야 합 니 다.그렇지 않 으 면 빈 지침 이상 을 알 릴 수 있 습 니 다.get 전에 set 를 호출 하지 않 아 도 정상적으로 접근 하려 면 initial Value()방법 을 다시 써 야 합 니 다
  • 3.ThreadLocal 의 응용 장면
    자바 에서 클래스 ThreadLocal 은 변수 가 서로 다른 스 레 드 간 의 격 리 성 을 해결 합 니 다.가장 흔히 볼 수 있 는 ThreadLocal 사용 장면 은 데이터베이스 연결 문제,Session 관리 등 이 있다.
    (1)데이터베이스 연결 문제
    
    private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
      public Connection initialValue() {
        return DriverManager.getConnection(DB_URL);
      }
    };
    
    public static Connection getConnection() {
      return connectionHolder.get();
    }
    
    
    (2)세 션 관리
    
    private static final ThreadLocal threadSession = new ThreadLocal();
    
    public static Session getSession() throws InfrastructureException {
      Session s = (Session) threadSession.get();
      try {
        if (s == null) {
          s = getSessionFactory().openSession();
          threadSession.set(s);
        }
      } catch (HibernateException ex) {
        throw new InfrastructureException(ex);
      }
      return s;
    }
    
    
    4.ThreadLocal 일반 사용 절차
    ThreadLocal 사용 절 차 는 일반적으로 세 단계 로 나 뉜 다.
  • ThreadLocal 대상 threadXxx 를 만 들 고 스 레 드 간 격 리 처리 대상 xxx 를 저장 합 니 다
  • 격 리 접근 할 데 이 터 를 가 져 오 는 방법 getXxx()를 제공 합 니 다.방법 에서 판단 합 니 다.ThreadLocal 대상 이 null 일 경우 new()격 리 접근 형식의 대상 이 어야 합 니 다
  • 4.567917.온라인 프로 세 스 류 의 run()방법 에서 getXxx()방법 을 통 해 조작 할 데 이 터 를 얻 을 수 있 습 니 다.그러면 모든 스 레 드 가 하나의 데이터 대상 에 대응 하고 그 어떠한 시간 에 도 이 대상 을 조작 하 며 교차 하지 않 습 니 다읽 어 주 셔 서 감사합니다. 여러분 에 게 도움 이 되 기 를 바 랍 니 다.본 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!

    좋은 웹페이지 즐겨찾기