本文是源码解析是基于
JDK 1.7
注意:
Android
内使用的ThreadLocal
跟JDK
内部的ThreadLocal
具体实现有区别。但是他们所做的功能是一致的,
只是Android
针对ThreadLocal
做了优化。但这不影响我们学习ThreadLocal的实现思想。
ThreadLocal
的使用相信大家都比较熟悉,但是ThreadLocal
内部是如何做到为不同线程保存不同的副本的呢?能看到这篇文章,
说明你也跟我一样好奇。接下来我们一层一层解开ThreadLocal
的面纱吧~1. 涉及到的几个重要类
ThreadLocal
里面的实现,主要涉及到以下几个重要类:
Thread:大家很熟悉的线程类,一个
Thread
类自然代表一个线程。ThreadLocal:既然本文是要解析
ThreadLocal
类,自然就离不开这个类啦~。ThreadLocalMap:可以看成一个
HashMap
,但是它本身具体的实现并没有实现继承HashMap
甚至跟java.util.Map
都沾不上一点关系。
只是内部的实现跟HashMap
类似(通过哈希表的方式存储)。ThreadLocalMap.Entry:把它看成是保存键值对的对象,其本质上是一个
WeakReference<ThreadLocal>
对象。主要涉及到的类暂时只有这些,其中
ThreadLocalMap
和ThreadLocalMap.Entry
的源码解析留到下一篇文章讲,
在本文中,我们暂时不去牵扯它们的实现细节,我们只需在心中默默地为ThreadLocalMap
打上HashMap
的标签,把它暂时先当成HashMap
来看待,ThreadLocalMap.Entry
看成是保存<键,值>
的对象。2. ThreadLocal数据存取
2.1 set函数
我们知道,在使用
ThreadLocal
时,首先创建ThreadLocal
对象,然后再调用其set(T)
、T get()
方法。我们从这些点切入,首先是构造函数如下:
public ThreadLocal() { }
可以看到,构造函数没有任何实现。接下来我们再从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); } 作者:huachao1001 链接:http://www.jianshu.com/p/a31f6d889647 來源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
代码整体流程很简单:
先拿到保存键值对的ThreadLocalMap
对象实例map
,如果map
为空(第一次调用的时候map
值为null
),则去创建一个ThreadLocalMap
对象并赋值给map
,并把键值对保存到map
中。
当然了,虽然整体流程看起来简单,其内部实现需要我们理清!
我们看到,首先是拿到当前线程实例t
,任何将t
作为参数获取ThreadLocalMap
对象。为什么需要通过Thread
类来获取ThreadLocalMap
对象呢?Thread
类和ThreadLocalMap
有什么联系?这些需要我们去看看getMap(Thread t)
函数的实现:
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
我们看到,getMap
的实现非常简单!!!仅仅返回Thread
实例的threadLocals
属性。Thread
中的ThreadLocalMap
属性声明如下:
ThreadLocal.ThreadLocalMap threadLocals = null;
我们先理一理,
ThreadLocal
的set(T)
函数中,首先是拿到当前线程Thread
对象中的ThreadLocalMap
对象实例threadLocals
,
然后再将需要保存的值保存到threadLocals
里面。换句话说,每个线程引用的
ThreadLocal
副本值都是保存在当前线程Thread
对象里面的。存储结构为ThreadLocalMap
类型,ThreadLocalMap
保存的键类型为ThreadLocal
,值为副本值
。2.2 get函数
有了
set
函数中的解析,我们对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) return (T)e.value; } return setInitialValue(); } 作者:huachao1001 链接:http://www.jianshu.com/p/a31f6d889647 來源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
同样的道理,拿到当前线程
Thread
对象实例中保存的ThreadLocalMap
对象map
,然后从map
中读取键为this
(即ThreadLocal
类实例)对应的值。如果
map
不是null
,直接从map
里面读取就好,如果map==null
,那么我们需要对当前线程Thread
对象实例中保存的ThreadLocalMap
对象new
一下。
即通过setInitialValue
函数来创建,setInitialValue
函数具体实现如下:
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; }
代码很简单,通过createMap
来创建ThreadLocalMap
对象,前面set
函数里面创建ThreadLocalMap
也是通过createMap
来的,我们看看createMap
具体实现:
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
这下对ThreadLocal
的存取机制彻底清楚了吧!接下来一节我们以图形的形式做个总结。
3. 各个类之间关系
ThreadLocal数据读取和设置过程
上图中绿色部分就是读取数据过程,橙色就是设置数据过程。
在下一篇文章中,我们将一起阅读ThreadLocalMap
源码,看看ThreadLocalMap
内部是如何实现类似Map
的功能的。
转载自:http://www.jianshu.com/p/a31f6d889647