概述
ThreadLocal是Java线程中的一种解决资源共享的方式,又叫线程本地存储,以根除对变量的共享来防止任务在共享资源上产生冲突。是一种自动化机制,可以为使用相同变量的每个不同的线程都创建不同的存储。其对象通常当做静态域存储,你只能通过get()
和set()
方法访问该对象的内容。
应用场景
ThreadLocal最常应用的地方多为数据库链接、Session管理的地方,例如封装Hibernate的模板代码时就会用到,当然还有我们之前讨论的Looper中也用到了。
源码分析
从上面的概述里我们知道,ThreadLocal只能使用get()
和set()
方法访问该对象的内容。那我们就先从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();
}
首先拿到当前线程,再用当前线程拿到一个ThreadLoacalMap
类,看一下这个类长什么样子:
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;
}
}
//到这里为止就是最重要的部分,后面的代码有兴趣可以自己去看
可以看到这个类主要就是有一个内部类Entry
来存储键值对,键为ThreadLocal
,值为Object
。
再来看看getMap(t)
方法。
/**
* 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;
}
直接返回Thread的threadLocals
属性,进去看一下这个属性:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
就是一个值为null
的ThreadLocaMap
,但是从threadLocals
这个属性的名字就可以看得出来,其中可以存储复数个Entry
的键值对。继续往下看。
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//注意这里
这里使用this
,就是取出键与当前ThreadLocal
对应的Entry
。
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
如果取出的Entry
不为空就直接返回对应的值。接下来看最后一个方法。
/**
* 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;
}
首先initialValue()
初始化一个值,接下来取Thread
和ThreadLocalMap
。如果map
不为空,直接将初始化的值填进去。如果map
为空,则调用createMap
。不管怎样,最终都会返回初始化的值。但是这个初始化方法返回的值默认是null
。所以如果不重写initialValue()
,则只能先set()
后get()
,否则会报空指针异常。
/**
* 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);
}
这个方法就是单纯的创建一个ThreadLocalMap
。
到这里,大家应该都明白ThreadLocal的大致结构了吧。
总结
- 每个线程中都有一个
ThreadLocalMap
的属性threadLocals
,其中可以存储复数个Entry
键值对,键为ThreadLocal
,值为Object
,用来存储用户变量副本。 - 如果不重写
initialValue()
,则只能先set()
后get()
,否则会报空指针异常。