java concurrency in practice读书笔记---ThreadLocal原理

来源:互联网 发布:淘宝内衣买家晒图 编辑:程序博客网 时间:2024/05/20 14:28

ThreadLocal这个类很强大,用处十分广泛,可以解决多线程之间共享变量问题,那么ThreadLocal的原理是什么样呢?源代码最能说明问题!

public class ThreadLocal<T> {    /**     * ThreadLocals rely on per-thread linear-probe hash maps attached     * to each thread (Thread.threadLocals and     * inheritableThreadLocals).  The ThreadLocal objects act as keys,     * searched via threadLocalHashCode.  This is a custom hash code     * (useful only within ThreadLocalMaps) that eliminates collisions     * in the common case where consecutively constructed ThreadLocals     * are used by the same threads, while remaining well-behaved in     * less common cases.     */    private final int threadLocalHashCode = nextHashCode();    /**     * The next hash code to be given out. Updated atomically. Starts at     * zero.     */    private static AtomicInteger nextHashCode =        new AtomicInteger();    /**     * The difference between successively generated hash codes - turns     * implicit sequential thread-local IDs into near-optimally spread     * multiplicative hash values for power-of-two-sized tables.     */    private static final int HASH_INCREMENT = 0x61c88647;    /**     * Returns the next hash code.     */    private static int nextHashCode() {        return nextHashCode.getAndAdd(HASH_INCREMENT);    }    /**     * 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;    }    /**     * Creates a thread local variable.     */    public ThreadLocal() {    }
ThreadLocal只有三个变量,从构造函数知道,在创建一个ThreadLocal实例时,只是调用nextHashCode方法将nextHashCode的值赋给实例的threadLocalHashCode,然后nextHashCode的值加HASH_INCREMENT。 因此ThreadLocal实例的变量只有threadLocalHashCode,而且是final的,用来区分不同的ThreadLocal实例。

再看关键的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);        if (map != null) {            ThreadLocalMap.Entry e = map.getEntry(this);            if (e != null)                return (T)e.value;        }        return setInitialValue();    }

其中调用getMap(Thread t)返回ThreadLocalMap,ThreadLocalMap是内部静态类,部分代码如下:

/**     * 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);        if (map != null) {            ThreadLocalMap.Entry e = map.getEntry(this);            if (e != null)                return (T)e.value;        }        return setInitialValue();    }

get方法的流程是这样的,首先获得当前线程引用,以当前线程为key,获得当前线程的ThreadLocalMap,也就是getMap方法,再查询ThreadLocalMap哈希表,查询当前线程的值。那么getMap方法是什么样的呢?

/**     * Get the map associated with a ThreadLocal. Overridden in     * InheritableThreadLocal.     *     * @param  t the current thread     * @return the map     */    ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }
也就是说当前线程中维持着一个ThreadLocalMap,这个Map里存放着这个线程的共享变量值,这就是真正的每个线程都拥有共享变量副本
那么这个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 expunged 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;            }        }        /**         * 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.         */        private Entry[] table;        /**         * The number of entries in the table.         */        private int size = 0;        /**         * The next size value at which to resize.         */        private int threshold; // Default to 0        /**         * Set the resize threshold to maintain at worst a 2/3 load factor.         */        private void setThreshold(int len) {            threshold = len * 2 / 3;        }        /**         * Increment i modulo len.         */        private static int nextIndex(int i, int len) {            return ((i + 1 < len) ? i + 1 : 0);        }        /**         * Decrement i modulo len.         */        private static int prevIndex(int i, int len) {            return ((i - 1 >= 0) ? i - 1 : len - 1);        }        /**         * Construct a new map initially containing (firstKey, firstValue).         * ThreadLocalMaps are constructed lazily, so we only create         * one when we have at least one entry to put in it.         */        ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {            table = new Entry[INITIAL_CAPACITY];            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);            table[i] = new Entry(firstKey, firstValue);            size = 1;            setThreshold(INITIAL_CAPACITY);        }        /**         * Construct a new map including all Inheritable ThreadLocals         * from given parent map. Called only by createInheritedMap.         *         * @param parentMap the map associated with parent thread.         */        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) {                    ThreadLocal key = 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++;                    }                }            }        }

ThreadLocalMap类是个内部静态类,其中的桶Entry类是继承WeakReference,也就是说,如果没有强引用引用key,那么在垃圾回收机制运行后,这些Entry将被回收。这说明ThreadLocal可以很好的被用于大量数据资源的共享。

那么,这里有个问题,共享变量如何拷贝给新建的线程呢?

看一下Thread类中关于ThreadLocalMap的代码:

 /* ThreadLocal values pertaining to this thread. This map is maintained     * by the ThreadLocal class. */    ThreadLocal.ThreadLocalMap threadLocals = null;    /*     * InheritableThreadLocal values pertaining to this thread. This map is     * maintained by the InheritableThreadLocal class.     */    ThreadLocal.ThreadLocalMap  /* ThreadLocal values pertaining to this thread. This map is maintained     * by the ThreadLocal class. */    ThreadLocal.ThreadLocalMap threadLocals = null;    /*     * InheritableThreadLocal values pertaining to this thread. This map is     * maintained by the InheritableThreadLocal class.     */    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
Thread类中声明了两个ThreadLocalMap变量

 /**     * Initializes a Thread.     *     * @param g the Thread group     * @param target the object whose run() method gets called     * @param name the name of the new Thread     * @param stackSize the desired stack size for the new thread, or     *        zero to indicate that this parameter is to be ignored.     */    private void init(ThreadGroup g, Runnable target, String name,                      long stackSize) {        if (name == null) {            throw new NullPointerException("name cannot be null");        }        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();        this.name = name.toCharArray();        if (security == null || isCCLOverridden(parent.getClass()))            this.contextClassLoader = parent.getContextClassLoader();        else            this.contextClassLoader = parent.contextClassLoader;        this.inheritedAccessControlContext = AccessController.getContext();        this.target = target;        setPriority(priority);        if (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();    }

这是Thread类的init方法,在创建一个Thread对象时,会调用这个方法进行一些初始化,在初始化操作中,有两句关于ThreadLocalMap的操作,就是调用ThreadLocalMap的构造方法,将父线程的ThreadLocalMap值复制到当前线程的ThreadLocalMap中!

也就是说,在Thread类中,两个ThreadLocalMap变量,分别存着当前共享变量的值和父线程的变量值。


原创粉丝点击