原文链接 了解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的具体实现略有不同。
发表回复