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();
        }
    };

两个方法都是重载了方法initialValue,只是第一种方法更加巧妙一些,其实本质都一样,详细区别可以见上篇文章

那在初始化对象执行线程中主要是通过

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中维护的map,如果这个map中已经有了这个值,则直接返回这个值,如果没有则通过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的整个调用过程了

下面需要讲讲InheritableThreadLocal是如何在父子类线程中传播对象的

首先看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);

如果这个线程的父类线程的inheritableThreadLocals 不为空,则调用

ThreadLocal.createInheritedMap(parent.inheritableThreadLocals)

这里就是把父类线程的值设置到子类线程中去的一个方法,那么看看InheritableThreadLocal如何保证inheritableThreadLocals不为空并且怎么实现createInheritedMap这个方法来实现值的复制的呢?

看看InheritableThreadLocal的源码

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);

就是调用了InheritableThreadLocal的childValue方法直接把传入的值返回出去,并且从getMap方法就可以看到当使用InheritableThreadLocal的时候获取的map就从之前的threadLocals变成了inheritableThreadLocals,通过定义好的inheritableThreadLocals来实现了父子类线程对象的传递。

以上就是所有源码解析的部分了,如有不理解的地方可以在下方留言哦~~~~

你可能感兴趣的:(java,ThreadLocal使用,ThreadLocal源码分析,父子类线程的传递,WeakReference)