ThreadLocal 의 소스 코드 깊이 분석

ThreadLocal 을 분석 하려 면 먼저 초기 화 부터 분석 합 니 다.전편 에서 초기 화 된 코드 는 다음 과 같 습 니 다.ThreadLocal 의 소개 와 예제 입 니 다.
private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> nextId.getAndIncrement());

private static final InheritableThreadLocal<Integer> threadId = new InheritableThreadLocal<Integer>() {
     
        protected Integer initialValue() {
     
            return nextId.getAndIncrement();
        }
    };

두 가지 방법 은 모두 방법 initial Value 를 다시 실 었 다.다만 첫 번 째 방법 은 더욱 교묘 할 뿐이다.사실은 본질 이 모두 같 고 상세 한 차 이 는 위의 글 을 볼 수 있다.
초기 화 대상 실행 스 레 드 에서 주로 통과 합 니 다.
MyThreadLocal.get();

대응 하 는 초기 화 값 을 가 져 옵 니 다.전체 소스 코드 의 분석 과정 은 이 get 방법 에서 시작 합 니 다.
public T get() {
     
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
     
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
     
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    
ThreadLocalMap getMap(Thread t) {
     
        return t.threadLocals;
    }

우선 ThreadLocal 에서 유지 하 는 맵 을 가 져 옵 니 다.이 맵 에 이 값 이 있 으 면 이 값 을 되 돌려 줍 니 다.없 으 면 setInitialValue 를 통 해 값 을 설정 합 니 다.여기 서 주의해 야 할 것 은 이 ThreadLocalMap 아래 ThreadLocalMap 의 일부 코드 를 보십시오.
static class ThreadLocalMap {
     

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expuniged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
     
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
     
                super(k);
                value = v;
            }
        }
        
		private Entry getEntry(ThreadLocal<?> key) {
     
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }
        

설명 으로 이해 하면 이 Entry 는 약 한 인용 형식 입 니 다.ThreadLocal 의 대상 이 비어 있 으 면 entry.get()도 비어 있 습 니 다.이 때 는 이 key 가 사용 되 지 않 는 다 는 것 을 의미 하기 때문에 이 entry 는 table 에서 지 울 수 있 습 니 다.이 entries 들 은 만 료 된 entries 라 고 불 릴 것 입 니 다.다음 코드 에 있 습 니 다.
ThreadLocalMap 의 전체 소스 코드 를 보면 쉽게 알 수 있 습 니 다.이 map 는 Entry 배열 로 데 이 터 를 저장 합 니 다.getEntry 방법 을 호출 할 때 먼저 key 의 hash 값 을 통 해 해당 Entry 배열 의 아래 표 시 를 찾 고 아래 표 시 를 통 해 해당 하 는 Entry 를 가 져 옵 니 다.e.get()을 호출 하면 대상 이 회수 되 었 다 고 생각 합 니 다.이때 회수 되 는 Reference 를 Entry 배열 에서 제거 하여 시스템 이 메모 리 를 회수 할 수 있 도록 해 야 합 니 다.
위의 getMap 방법 을 보면 이 스 레 드 가 가지 고 있 는 threadLocals 를 통 해 이 ThreadLocal 에 대응 하 는 value 가 있 는 지 찾 고 있 습 니 다.있 으 면 되 돌아 오고 없 으 면 초기 화 됩 니 다.초기 화 코드 는 다음 과 같 습 니 다.
private T setInitialValue() {
     
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

비교적 간단 해 보 입 니 다.다시 불 러 온 initialValue()를 호출 하여 대응 하 는 value 를 얻 은 다음 set 를 대응 하 는 map 대상 에 가 져 옵 니 다.위 는 ThreadLocal 의 전체 호출 과정 입 니 다.
다음은 Inheritable Thread Local 이 어떻게 부자 류 스 레 드 에서 대상 을 전파 하 는 지 에 대해 이야기 해 야 합 니 다.
먼저 Thread 의 원본 코드 를 보고 init 의 방법 을 보 세 요.(이 방법 은 스 레 드 가 초기 화 되 는 과정 에서 호출 됩 니 다)
private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
     
        if (name == null) {
     
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;

        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
     
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
     
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
     
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
     
            if (isCCLOverridden(getClass())) {
     
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

마지막 부분 을 주로 봤 어 요.
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

이 스 레 드 의 부모 스 레 드 의 inheritable ThreadLocals 가 비어 있 지 않 으 면 호출 합 니 다.
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals)

이것 이 바로 부모 스 레 드 의 값 을 하위 스 레 드 에 설정 하 는 방법 입 니 다.그러면 Inheritable Thread Local 이 inheritable Thread Locals 가 비어 있 지 않 고 createInherited Map 이라는 방법 으로 값 의 복 제 를 어떻게 실현 하 는 지 보 시 겠 습 니까?
Inheritable Thread Local 의 소스 코드 를 보 세 요.
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
     
    /**
     * Computes the child's initial value for this inheritable thread-local
     * variable as a function of the parent's value at the time the child
     * thread is created.  This method is called from within the parent
     * thread before the child is started.
     * 

* This method merely returns its input argument, and should be overridden * if a different behavior is desired. * * @param parentValue the parent thread's value * @return the child thread's initial value */

protected T childValue(T parentValue) { return parentValue; } /** * Get the map associated with a ThreadLocal. * * @param t the current thread */ ThreadLocalMap getMap(Thread t) { return t.inheritableThreadLocals; } /** * Create the map associated with a ThreadLocal. * * @param t the current thread * @param firstValue value for the initial entry of the table. */ void createMap(Thread t, T firstValue) { t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); } }

ThreadLocal 의 일부 소스 코드 를 보고 있 습 니 다.
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
     
        return new ThreadLocalMap(parentMap);
    }

createInheritedMap 을 호출 하여 ThreadLocalMap 대상 으로 되 돌 아 왔 습 니 다.
private ThreadLocalMap(ThreadLocalMap parentMap) {
     
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
     
                Entry e = parentTable[j];
                if (e != null) {
     
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
     
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

그리고 이 방법 중 에...
Object value = key.childValue(e.value);

바로 Inheritable ThreadLocal 의 childValue 방법 을 호출 하여 들 어 오 는 값 을 직접 되 돌려 주 고 getMap 방법 에서 Inheritable ThreadLocal 을 사용 할 때 얻 은 map 는 이전의 thread Locals 에서 inheritable ThreadLocals 로 바 뀌 었 으 며 정 의 된 inheritable ThreadLocals 를 통 해 부자 류 스 레 드 대상 의 전달 을 실현 했다.
이상 은 모든 소스 코드 해석 부분 입 니 다.이해 하지 못 하 는 부분 이 있 으 면 아래 에 댓 글 을 남 겨 주세요~~~

좋은 웹페이지 즐겨찾기