Rumah >Java >javaTutorial >ThreadLocal原理浅析
ThreadLocal即线程局部变量的意思!所以什么是线程局部变量?这玩意有什么鸟用?是不是面试被问到了说不出个一二三?今天就来扒一扒这货的源码,从根本上了解这货是干啥的。
Thread、ThreadLocalMap、Entry三者关系
其实研究下来他的源码实现,其实也没想象的那么复杂,其最主要有以下几点:
1、Java可以通过Thread.currentThread()来获得当前的Thread的实例对象。既然能拿到这Thread对象实例,那么我们就可以操作该实例(的属性),比如为该Thread对象设置一个值什么。
2、每一个Thread对象都有一个ThradLocalMap实例,该实例有一个一个Entry组成的数组,Entry对象有两个主要属性:value和ThreadLocal的弱引用,其中value这个属性就是值设置给当前线程所持有,也是ThreadLocal的核心属性:
static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
注意Entry继承自WeakReference,其key就是ThreadLocal对象!(图1)
结合1和2两个知识点,我们就可以知道我们拿到Thread对象之后,就可以操控当前线程对象的ThreadLocalMap对象,然后把想要保存的value交给ThreadLocalMap的Entry的value属性,Thread,ThreadLocalMap,value三者之间的关系可以用下图表示(图2):
通过上图我们可以得出这么一个结论:一个Thread对象持有一个ThreadLocalMap对象,然后呢,一个ThreadLoalMap对象又包含了多个ThreadLlocal对象及ThreadLocal对象所在线程的value!!!一言以蔽之: 一个Thread对象可以持有多个ThreadLocal对象的变量值value
那么ThreadLocal和Thread又有啥关系呢?二者是怎能对value进行读取的呢?下面就根据源码来简单的分析下。
ThreadLocal和Thread的关联
先看看ThreadLocal的set方法:
public void set(T value) { //获取当前线程 Thread t = Thread.currentThread(); //获取当前线程持有的ThreadLocalMap ThreadLocal.ThreadLocalMap map = getMap(t); //将value设置给threadlocalMap if (map != null) map.set(this, value); else createMap(t, value); }
set方法很逻辑很简单(j结合上图2看更好理解):
1、通过currentThread方法拿到当前Thread对象
2、获取当前Thread对象的ThreadLoalMap对象
3、将value连同ThreadLocal对象自己组成一个Entry对象保存在
ThreadLoalMap的Entry类型的数组中。
在来看看ThreadLocal的get方法:
public T get() { //获取当前线程 Thread t = Thread.currentThread(); //获取当前线程的ThreadLocalMap对象 ThreadLocalMap map = getMap(t); if (map != null) { //获取与ThreadLocal对象想关联的value ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") //获取值 T result = (T)e.value; return result; } } //为空返回初始化值 return setInitialValue(); }
可以发展get的整体逻辑也很简单:
1、获取当前Thread对象
2、获取当前Thread对象的ThreadLocalMap对象
3、从ThreadLocalMap中获取与ThreadLocal相关联的Entry对象,具体的就是以ThreadLocal为key获取。
4、获取步骤3的Entry的value属性,并返回之。
通过整体观察get和set方法可以得出如下结论:ThreadLocal对象调用set方法就是往Thread对象的ThreadLocalMap里面添加值;ThreadLocal对象调用get方法就是从Thread对象的ThreadLocalMap里面获取值。核心就是操纵Thread对象的ThreadLocalMap对象进行value的读和写。原理就这么简单。
那么位于不同线程的不同ThreadLocal对象,在其他线程里保存值是一个什么样的关系呢?可以通过下图来清晰的描述出来:
ThreadLocal的使用实例
我们在知道在Android中一个线程只有一个Looper对象,那么是怎么做到的呢?就是ThreadLocal发挥了作用,看看Looper的prepare方法:
//定义一个静态的ThreadLocal变量 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static void prepare(boolean quitAllowed) { //一个Thread只能关联一个Looper对象 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
观察prepare方法可以知道,先通过sThreadLocal的get方法判断当前线程是否已经拥有了一个Looper对象,如果有就抛出一个异常;如果当前线程还没有设置Looper对象,则调用ThreadLocal的set方法,初始化一个Looper对象交给当前线程:
sThreadLocal.set(new Looper(quitAllowed));
这样就确保了一个线程只有一个Looper对象。
到此为止,关于ThreadLocal的原理已经基本分析完毕,至于内部是怎么set和get的,博主并没有做太细的分析,因为没必要,了解ThreadLocal的工作原因以及使用场景即可。
Atas ialah kandungan terperinci ThreadLocal原理浅析. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!