ThreadLocal的用法
ThreadLocal用于保存某个线程共享变量:对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。
ThreadLocal.get: 获取ThreadLocal中当前线程共享变量的值。
ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值。
ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。
ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值。
示例代码:
1 | private static final ThreadLocal<String> threadLocal = new ThreadLocal<>(); |
可见每个线程从ThreadLocal中取出的值,都是自身放入的值。
ThreadLocal的实现原理
为了加深理解,简单看看ThreadLocal的实现原理。
set()的源码是这样子的:
1 | public void set(T value) { |
通过源码我们知道value是存放在了ThreadLocalMap里了。这个ThreadLocalMap是从哪里取出来的,我们看一下源码中的getMap(t)方法:
1 | ThreadLocalMap getMap(Thread t) { |
也就是说这个ThreadLocalMap是我们当前Thread中的一个叫threadLocals的成员变量,ThreadLocalMap是由当前线程自身持有的。
当这个map为null时,说明当前线程还没创建过ThreadLocalMap,所以通过createMap(t,value)创建ThreadLocalMap并放入线程中。
对set方法进行总结一下: 通过当前线程对象thread获取该thread所维护的threadLocalMap,若threadLocalMap不为null,则以threadLocal实例为key,值为value的键值对存入threadLocalMap,若threadLocalMap为null的话,就新建threadLocalMap然后在以threadLocal为键,值为value的键值对存入即可。
get()方法和remove()实际上也都是调用了这个当前Thread中维护的threadLocalMap。这里就不多做解释了。
ThreadLocal的使用场景
ThreadLocal 不是用来解决共享对象的多线程访问问题的,数据实质上是放在每个thread实例引用的threadLocalMap,也就是说每个不同的线程都拥有专属于自己的数据容器(threadLocalMap),彼此不影响。因此threadLocal只适用于 共享对象会造成线程安全 的业务场景。比如hibernate中通过threadLocal管理Session就是一个典型的案例,不同的请求线程(用户)拥有自己的session,若将session共享出去被多线程访问,必然会带来线程安全问题。