ThreadLocal
是什么?
线程变量,往ThreadLocal中填充的变量属于线程私有的。
public static void main(String[] args) {
ThreadLocal<String> tl = new ThreadLocal<>();
for (int i = 0; i < 20; i++) {
new Thread(() -> {
tl.set(Thread.currentThread().getName() + "存入的值");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "获取到" + tl.get());
System.out.println();
}).start();
}
}
ThreadLocal中的set方法
可以看出,当调用set方法时,会先判断是否已经存在ThreadLocalMap了,如果,存在,直接往里面set值,如果不存在,会去创建一个ThreadLocalMap。
ThreadLocalMap的createMap方法
可以看出,是在这里创建了一个ThreadLocalMap,并且将ThreadLocal的引用地址作为key。而创建出来的ThreadLocalMap是放到了Thread中的成员变量threadLocals,所以这也保证了每个线程获取到的threadLocals是属于本线程的。
ThreadLocalMap的构造器
ThreadLocalMap的构造函数里面其实是创建了一个初始容量为16的Entry数组进行数据存储,并且数据存放在数组中的索引位置是根据hashCode和length位运算得出。上面我们讲过每个线程其实都有一个单独的ThreadLocalMap实例threadLocals,结合此处可以理解为每个线程持有一个Entry类型的数组table,我们所有的读写过程其实都是操作这个数组完成的。
ThreadLocalMap的set方法
遍历table数组,如果查询到数组中有该ThreadLocal对应的值,那么将新值覆盖旧值;如果table数组未满,那么将值放到table数组的空位上。
ThreadLocal的get方法
ThreadLocal未获取到值情况下初始化值方法
根据该ThreadLocal从table中获取到对应的Entry,如果有值,将值返回回去;如果没值或者ThreadLocalMap没有获取到的话,那么先给Thread初始化一个ThreadLocalMap对象,然后给对应的的索引处存一个null进去,将该null返回回来即可。
ThreadLocal特性
ThreadLocal和Synchronized都是为了解决多线程中相同变量的访问冲突问题,不同的点是
1、Synchronized是通过线程等待,牺牲时间来解决访问冲突
2、ThreadLocal是通过每个线程单独一份存储空间,牺牲空间来解决冲突,并且相比于Synchronized,ThreadLocal具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。
正因为ThreadLocal的线程隔离特性,使他的应用场景相对来说更为特殊一些。在android中Looper、ActivityThread以及AMS中都用到了ThreadLocal。当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal。