程序员

正确解读ThreadLocal

2016-07-15  本文已影响287人  SilenceDut

在Android的消息机制中,Handler是非常重要的一部分,而完全要理解Handler的机制,首先应该理解ThreadLocal,关于ThreadLocal,见到很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。可能很多人都知道ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量,这样的词容易让人产生误解或者迷惑。
首先,从最新的ThreadLocal源码看,ThreadLocal并未创建任何本地变量,也没有copy副本的存在,是直接用的Thread对象的成员变量,因此叫做"线程变量帮助类"其实更合适,它的作用就是拿到当前线程对象的Object[] value数组,然后进行存储和取值,因为这属于每个线程的内部变量数组,因此也不存在共享,所以也就没有线程安全的问题。
先看一个例子:

ThreadLocal例子.png

例子可以看出不同的线程得到的值是不同的,说明ThreadLocal可以使同一个变量在不同的线程里有不同的值,为什么同一个变量在不同的线程的会表现出不同的值呢,源码说明一切:

先看set方法:

set(T value).png 得到当前Thread对象的Values值.png

可以看出ThreadLocal的Values引用直接指向Thread的localValues值。看下put()方法的实现。

put.png

很好理解,可以简单看做用单个数组来实现的简易hashmap的,hashmap的key是当前ThreadLocal对象的hash值与当前数组长度的求模运算,存入在数组的index位置,value就是当前的存入值,这个值总是放在index+1的位置,可以理解为index和index+1这两个位置就是hashmap的Entry。好像在jdk1.7之前就是用hashmap来实现的,原理都是一样的。这样是Thread类更加的轻量化。

get()

通过上面的分析get函数也很好理解了。先得到当前线程对象的Values对象,然后得到Values中的Object[] table数组,从数组中取出值。

ThreadLocal 实例通常建议是用 private static 字段,至于原因想不太清楚。但这不是绝对的,在Android的事件机制Looper中

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

这就不是一个private变量,至于静态

public static @Nullable Looper myLooper() {    
    return sThreadLocal.get();
}

因为需要静态方法获取Looper对象,所以就必须是静态的的吧。看到一种说法是设置static 是因为ThreadLocal支持线程范围生命周期的变量,所以不属于类的属性。不知是否有些牵强。
关于内存泄露的问题

/** Weak reference to this thread local instance. */
private final Reference<ThreadLocal<T>> reference = new WeakReference<ThreadLocal<T>>(this);

因为是软引用持有,所以不会存在内存泄露的问题。但确定不需要使用的时候最好调用remove()方法来释放内存。

简单总结

判断是否需要对资源进行同步的判断准则是,当前获取(get)资源是否会有其他线程进行修改(set)或者当前进行修改的资源是否会有其他线程可以获取。

上一篇 下一篇

猜你喜欢

热点阅读