자바 병렬 프로 그래 밍 시리즈(6)-ThreadLocal 사용 및 소스 코드 분석

21278 단어 자바 다 중 루틴
ThreadLocal 은 스 레 드 로 컬 변수 가 되 었 습 니 다.이름 에서 스 레 드 로 컬 변수 와 관련 이 있 는 것 으로 짐작 할 수 있 습 니 다.제공 하 는 방법 은 주로 몇 가지 가 있 습 니 다.
//   
public T get() { }
//   
public void set(T value) { }
//  
public void remove() { }
//    
protected T initialValue() { }

ThreadLocal 사용
ThreadLocal 을 어떻게 사용 하 는 지 봅 시다.
package com.rancho945.concurrent;


public class Test {

    public static void main(String[] args) {
        final ThreadLocal local = new ThreadLocal();
        //             
        local.set("hello world");
        new Thread(new Runnable() {

            @Override
            public void run() {
                //                      local 
                System.out.println("1."+Thread.currentThread().getName()+"---"+local.get());
                //        
                local.set("hello ThreadLocal");
                //       
                System.out.println("2."+Thread.currentThread().getName()+"---"+local.get());
                //  
                local.remove();
                //  
                System.out.println("3."+Thread.currentThread().getName()+"---"+local.get());
            }
        }).start();
        //         
        try {
            Thread.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //        
        System.out.println("4."+Thread.currentThread().getName()+"---"+local.get());
    }

}

실행 결과
1.Thread-0---null
2.Thread-0---hello ThreadLocal
3.Thread-0---null
4.main---hello world

이 를 통 해 알 수 있 듯 이 local 인 스 턴 스 는 두 개의 스 레 드 가 공유 되 지만 그들 사이 에 값 을 설정 하고 값 을 추출 하 는 것 은 서로 영향 을 주지 않 습 니 다.이것 이 바로 로 컬 스 레 드 변수 입 니 다.initial Value 를 어떻게 사용 하 는 지 보고 있 습 니 다.위의 예 에서...
final ThreadLocal local = new ThreadLocal();

...로 바꾸다
final ThreadLocal local = new ThreadLocal(){
            @Override
            protected String initialValue() {
                // TODO Auto-generated method stub
                return "hehe";
            }
        };

그러면 실행 결 과 는:
1.Thread-0---hehe
2.Thread-0---hello ThreadLocal
3.Thread-0---hehe
4.main---hello world

initialValue 를 다시 썼 다 면 스 레 드 에 값 이 설정 되 어 있 지 않 거나 제거 되 었 을 때 initialValue 를 기본 값 으로 되 돌려 줍 니 다.
소스 코드 분석
ThreadLocal 클래스 에는 내부 클래스 ThreadLocalMap 이 있 습 니 다.ThreadLocal 과 스 레 드 로 컬 변수 간 의 관 계 를 담당 합 니 다.Thread 클래스 는 ThreadLocalMap 류 의 구성원 변 수 를 가지 고 있 습 니 다.
ThreadLocal 클래스 의 멤버 변 수 를 보 세 요.
//  Thread       
private final int threadLocalHashCode = nextHashCode();
//          
private static AtomicInteger nextHashCode = new AtomicInteger();
//     ,              ,               2^N    ,          ,   。
private static final int HASH_INCREMENT = 0x61c88647;
//            0x61c88647,         。
private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }

set(T value)방법
ThreadLocal 클래스 의 set 방법 보기:
public void set(T value) {
    //      
    Thread t = Thread.currentThread();
    //     TheadLocalMap
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

Thread 에서:
ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocalMap 은 ThreadLocal 의 정적 내부 클래스 입 니 다.처음에 스 레 드 를 가 져 온 map 는 null 입 니 다.즉,스 레 드 의 thread Locals 는 초기 화 지연 입 니 다.스 레 드 가 ThreadLocal 기능 을 사용 하지 않 았 을 때 대상 을 만 들 지 않 고 번 거 로 움 을 피 하 며 JDK 의 깊 은 인 코딩 기 초 를 충분히 나 타 냈 습 니 다.우 리 는 먼저 createMap 의 실현 을 살 펴 보 자.
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

현재 스 레 드 를 만 드 는 threadLocals 변수 입 니 다.여기 들 어 오 는 첫 번 째 매개 변 수 는 this 입 니 다.즉,현재 threadlocal 인 스 턴 스 대상 입 니 다.ThreadLocalMap 내부 클래스 보기
 /**
 * The initial capacity -- MUST be a power of two.
 */
 //        
private static final int INITIAL_CAPACITY = 16;

/**
 * The table, resized as necessary.
 * table.length MUST always be a power of two.
 */
 //             ,Entry         ,  threadloacl         
private Entry[] table;

/**
 * The number of entries in the table.
 */
 //          
private int size = 0;

/**
 * The next size value at which to resize.
 */
 //         table  size    
private int threshold; // Default to 0

//       ThreadLocal         ,          ThreadLocal    
static class Entry extends WeakReference> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal> k, Object v) {
        super(k);
        value = v;
    }
}
ThreadLocalMap(ThreadLocal> firstKey, Object firstValue) {
    //tabale    ,   INITIAL_CAPACITY
    table = new Entry[INITIAL_CAPACITY];
    //            0x61c88647  ,           threadlocal        ,
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    //   i     threadlocal        k-v    
    table[i] = new Entry(firstKey, firstValue);
    //      
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}

private void setThreshold(int len) {
    //        size len     
    //         16,  16*2/3=10   table  
    threshold = len * 2 / 3;
}

현재 스 레 드 에 ThreadLocalMap 인 스 턴 스 를 만 들 고 현재 threadlocal 대상 과 넣 어야 할 값 관 계 를 ThreadLocalMap 인 스 턴 스 의 table 에 저장 하 는 것 이 주요 작업 입 니 다.만약 에 하나의 스 레 드 가 세 개의 threadlocal 대상 을 사용 했다 면 예 를 들 어:
final ThreadLocal local1 = new ThreadLocal();
final ThreadLocal local2 = new ThreadLocal();
final ThreadLocal local3 = new ThreadLocal();
new Thread(new Runnable() {

    @Override
    public void run() {
        local1.set("ThreadLocal1");
        local2.set("ThreadLocal2");
        local3.set("ThreadLocal3");
    }
}).start();

그러면 local 1 과'Thread Local 1'은 하나의 entry 로 스 레 드 thread Locals(ThreadlocalMap 인 스 턴 스)변수의 table 에 놓 여 있 고,local 2 와'Thread Local 2'는 하나의 entry 로 도 이 table 에 놓 여 있 으 며,local 2 와'Thread Local 2'는 하나의 entry 로 도 table 에 놓 여 있 습 니 다.물론 뒤에 있 는 두 개의 entry 는 create 방법 에 넣 은 것 이 아 닙 니 다.처음 create 를 한 후에 getMap 방법 은 null 로 돌아 가지 않 고 map.set()를 실행 하 는 것 이 라 고 생각 합 니 다.
public void set(T value) {
    //      
    Thread t = Thread.currentThread();
    //     TheadLocalMap
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

map.set()보기:
/**
 * Set the value associated with key.
 *
 * @param key the thread local object
 * @param value the value to be set
 */
private void set(ThreadLocal> key, Object value) {

    // We don't use a fast path as with get() because it is at
    // least as common to use set() to create new entries as
    // it is to replace existing ones, in which case, a fast
    // path would fail more often than not.

    Entry[] tab = table;
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1);
    //   table i     ,    entry  
    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
        ThreadLocal> k = e.get();
        //     key     key  ,       threadlocal,        ,  
        if (k == key) {
            e.value = value;
            return;
        }
        //     key null,       ,            k-v  ,    ,            
        if (k == null) {
            replaceStaleEntry(key, value, i);
            return;
        }
    }
    //     table   ,   i         。
    tab[i] = new Entry(key, value);
    int sz = ++size;
    //            Threadlocal   table    ,          。
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}


private static int nextIndex(int i, int len) {
    return ((i + 1 < len) ? i + 1 : 0);
}

get()방법
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        //    threadlocal    ThreadLocalMap  entry
        ThreadLocalMap.Entry e = map.getEntry(this);
        //         
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    //    
    return setInitialValue();
}
 private T setInitialValue() {
    //    
    T value = initialValue();
    //        set    
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}
//    ThreadLocal       ,      
protected T initialValue() {
    return null;
}

이것 은 앞에서 set 전이 나 remove 가 없 으 면 initial Value 방법의 반환 값 을 되 돌려 주 는 이 유 를 설명 한다.
remove()방법
ThreadLocal 의 remove 방법:
 public void remove() {
     ThreadLocalMap m = getMap(Thread.currentThread());
     if (m != null)
         m.remove(this);
 }

ThreadLocalMap 을 호출 하 는 방법 을 볼 수 있 습 니 다.
 /**
 * Remove the entry for key.
 */
private void remove(ThreadLocal> key) {
    Entry[] tab = table;
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1);
    //  entry
    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
         //     ,   
        if (e.get() == key) {
            //    entry  threadlocal     
            e.clear();
            //      table i   entry
            expungeStaleEntry(i);
            return;
        }
    }
}

사고 하 다.
다음 코드 실행 이 무엇 을 출력 하 는 지 보 세 요.
package com.rancho945.concurrent;

public class Counter {
    public int count = 0 ;

    public void increment(){
        count++;
    }
}
package com.rancho945.concurrent;


public class Test {

    public static void main(String[] args) {
        final ThreadLocal local = new ThreadLocal();
        final Counter counter = new Counter();
        //             ,  count  0
        local.set(counter);
        //       
        counter.increment();
        new Thread(new Runnable() {

            @Override
            public void run() {

                local.set(counter);
                //       local   count
                System.out.println(local.get().count);
            }
        }).start();

    }
}

생각해 보 세 요.집행 결 과 는 무엇 입 니까?0 인가요?답 은 1.이것 은 시작 하 는 예 와 왜 다 릅 니까?혹시 가짜 ThreadLocal 을 썼 나 요?서로 다른 스 레 드 간 에 서로 영향 을 주지 않 는 다 고 했 나 요?
우 리 는 여기 가 시작 하 는 것 과 무엇이 다른 지 보 았 다.시작 하 는 예 는 메 인 스 레 드 와 서브 스 레 드 set 의 대상 이 다 르 고 여기 set 는 같은 대상 이다.
즉,threadlocal 설정 이 서로 다른 대상 스 레 드 간 에 서로 영향 을 주지 않 고 같은 대상 이 라면 서로 영향 을 줄 수 있다 는 것 이다.스 레 드 복사 본 은 set 가 같은 대상 이 아니 라 이 대상 을 복사 할 수 있다 는 뜻 입 니 다.실질 적 으로 여전히 같은 대상 이다.

좋은 웹페이지 즐겨찾기