原文链接 了解Java ThreadLocal

ThreadLocal是Java提供的为每个线程存储线程独立的数据的存储形式,也就是说用ThreadLocal来保留的数据,只能被以后线程所拜访,其余线程无法访问,因为只有(一个线程)以后线程可能拜访,所以它是线程平安的,能够用来存储一些不能被共享的数据。

根本应用办法

ThreadLocal应用起来十分的简略,它反对泛型,能够把任意类型的数据放进ThreadLocal,一个ThreadLocal对象只能放一个对象:

ThreadLocal<String> mLocalCEOHolder = new ThreadLocal<>();ThreadLocal<Integer> mOrdersCountHolder = new ThreadLocal<>();mLocalCEOHolder.set("Alex Hilton");String ceo = mLocalCEOHolder.get();mOrdersCountHolder.set(30249);int order = mOrdersCountHolder.get();

实现原理解析

就按下面的例子来解析它的实现原理:

  • 创立ThreadLocal对象

    先来看看它的构造方法:

/** * Creates a thread local variable. * @see #withInitial(java.util.function.Supplier) */public ThreadLocal() {}

很可怜,它的构造方法是空的,啥也没干。

  • set办法
    再来看下它的set办法:
/** * Sets the current thread's copy of this thread-local variable * to the specified value.  Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of *        this thread-local. */public void set(T value) {    Thread t = Thread.currentThread();    ThreadLocalMap map = getMap(t);    if (map != null)        map.set(this, value);    else        createMap(t, value);}

这里先获取以后的调用线程,从其中获取一个叫做ThreadLocalMap的货色,如果它不为空就把以后对象this(ThreadLocal对象)作为key,把要寄存的值作为value,放到这个ThreadLocalMap外面,如果map为空就先创立再寄存。由此能够猜出ThreadLocalMap是一个Map型的数据结构,接着钻研getMap和createMap,前面再具体说ThreadLocalMap。

/** * 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;}/** * 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 */void createMap(Thread t, T firstValue) {    t.threadLocals = new ThreadLocalMap(this, firstValue);}

getMap比较简单,它返回Thread对象的域对象threadLocal。createMap也很简略创立一个ThreadLocalMap对象,而后把它赋值给Thread对象的域变量。

  • get办法
    再来看看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) {            @SuppressWarnings("unchecked")            T result = (T)e.value;            return result;         }    }    return 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();    Thread t = Thread.currentThread();    ThreadLocalMap map = getMap(t);    if (map != null)        map.set(this, value);    else        createMap(t, value);    return value;}

get办法与set办法相似,同样是从以后线程取其map,从其中以以后TheadLocal对象为key来查找值,如果找到了,就返回。如果map为空,或者没找到怎么办。就用setInitialValue来初始化线程的map对象,这个与set办法是一样的,只不过用空值(null)。

  • ThreadLocalMap对象
/** * ThreadLocalMap is a customized hash map suitable only for * maintaining thread local values. No operations are exported * outside of the ThreadLocal class. The class is package private to * allow declaration of fields in class Thread.  To help deal with * very large and long-lived usages, the hash table entries use * WeakReferences for keys. However, since reference queues are not * used, stale entries are guaranteed to be removed only when * the table starts running out of space. */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;            /* other codes ... */}

残缺的就不贴了,大家能够本人去查看。简略来了解,其实它就是一个HashMap,key是对ThreadLocal对象的WeakReference,value是咱们放入ThreadLocal的对象。

到这里能够总结一下ThreadLocal的原理了:数据结构是存储在线程对象里的一个Map对象中,key是ThreadLocal对象的WeakReference,值就是咱们想要寄存的对象。

留神:上面提到的Map的意思是Thread#threadLocals,也就是ThreadLocalMap对象

外围所在

网下面很多对于ThreadLocal的文章(如这个和这个)都没有讲清楚,到底它是用什么办法来保障只有以后线程能力拜访,它们说的是都是它的数据结构,这个下面曾经说了。然而光有一个Map,就够了吗?Thread对象有一个Map用来存储ThreadLocal数据,然而如果Thread有公开获取此Map的办法,那跟咱们应用的共享变量有什么区别?

ThreadLocal的真正外围在于它取的以后线程的Map:

  • 每次从ThreadLocal取数据也好,放数据也好,指标的Map都是以后的线程的Map
  • 线程的Map是包拜访权限
  • 放数据也好,取数据也好都是从以后线程的Map里存和取

所以,ThreadLocal最要害的就是由Thread.currentThread()来保障以后线程的。线程到底是什么?线程简略来了解就是一个run办法,或者说一堆办法调用,它是一个时序的概念,是一堆按某种程序运行的指令的汇合。所以,当你调用ThreadLocal#set或者ThreadLocal#get时,在set和get办法实现外面会调用Thread.currentThread来获得调用栈所在的线程---也就是以后线程,这也就保障了,一个线程只能获取本人的Map。

另外,Map必须得与每个线程对象绑定,但又因为这个域是package作用域,只有同一个package的能力拜访,所以只能从ThreadLocal里操作此Map也是绝对平安的,也就是说想操作此Map只能通过ThreadLocal。当然了,如果能Hack一下,生成一个与Thread在同一个package的对象,就可能间接操作Map,也就能突破ThreadLocal的封装了,这时Map就变成可共享的了,也就失去了线程独有的个性。

典型利用

最典型的利用要数Looper类的实现。

Looper的作用是帮忙线程创立并运行一个音讯循环(MessageQueue),很显然,一个线程有且只能有一个,那么ThreadLocal是最佳的计划。

Android当中的实现

与规范的Java的实现原理是一样的,都是把Map当作Thread的一个域,package作用域,ThreadLocal作为key, 外面的值作value。

只不过Map的具体实现略有不同。

原创不易,打赏点赞在看珍藏分享 总要有一个吧