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