ThreadLocal简析
首先先来看一段代码
1 | public class ThreadId { |
然后控制台输出如下:
1 | 1 |
不难发现,不同的线程获取到的threadId是不一样的,那么为什么会出现这种情况呢,所以需要分析下ThreadLocal这个类,看看threadId.get()的逻辑
1 | public T get() { |
第一步获取当前线程实例,然后调用getMap(t)获取到ThreadLocalMap实例
1 | ThreadLocalMap getMap(Thread t) { |
getMap(Thread t)方法直接返回了当前线程实例中的threadLocals成员变量,继续
1 | ThreadLocal.ThreadLocalMap threadLocals = null; |
其实threadLocals是ThreadLocal中的静态内部类ThreadLocalMap类型,那么此时返回为null,接着调用return setInitialValue();
1 | private T setInitialValue() { |
此时调用了重写的initialValue()方法进行nextId的自增操作,接着调用createMap(t, value);
1 | void createMap(Thread t, T firstValue) { |
此时创建了一个ThreadLocalMap实例并复制给当前线程的threadLocals变量,再来看其创建过程
1 | ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { |
这里创建了一个初始长度为INITIAL_CAPACITY即长度为 16 的Entry类型数组,然后通过firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);获取到数组下标,创建new Entry(firstKey, firstValue)赋值到table的i位置,size记录的是数组中被赋值的个数,setThreshold(INITIAL_CAPACITY);是计算threshold的大小,即INITIAL_CAPACITY * 2 / 3,当size达到这个值时,table数组将会被扩容。接着我们来看看Entry
1 | static class Entry extends WeakReference<ThreadLocal> { |
Entry是ThreadLocal的静态内部类,利用虚引用保存ThreadLocal实例,另外还保存了value值。get方法的流程大致就是如此了,首先会获取当前类中的ThreadLocalMap类型的threadLocals成员变量,如果为空则调用初始化方法,并且创建一个ThreadLocalMap实例赋值给当前线程,然后经过取余计算获取对应数组的位置创建Entry实例并赋值。ThreadLocal在Android中也有应用,在Handler机制中,当我们调用Looper.prepare();创建Looper的时候用到了ThreadLocal存储。
1 | public static void prepare() { |
从代码中可以看出,一个线程最多只能创建一个Looper实例,不然会抛出异常,那么我们接下里就来分析下ThreadLocal的set方法
1 | public void set(T value) { |
逻辑和get方法一样先获取当前线程的threadLocals,如果为空和上面的流程一样,这里就不再讲了,接下来看看map.set(this, value);
1 | private void set(ThreadLocal key, Object value) { |
大致就是先根据key即ThreadLocal实例的threadLocalHashCode获取到需要赋值数组的位置,然后判断是否有实例存在,有则覆盖value值,没有则创建一个Entry实例赋值到对应位置,最后判断需不需要resize…ThreadLocal在我看来其实就是作用于线程范围的类,适用于不同的线程需要创建不同的数据副本情况,比如说Android中的Looper,暂时还未想到在Andriod实际开发中需要用到ThreadLocal的业务场景。