1、Threadlocal 背景、原理
背景:对于一个变量,如果多个线程需要操作该变量该怎么做,而且该变量在各个线程内还要代表不同的值(线程间不需要据此变量通信)
对于上述问题,首先可以加锁解决,但是总感觉不够灵活,浪费资源;其实这就可以用 ThreadLocal
原理:先写个例子再理解原理比较好(如下)
这里就简单举例(每个线程在自己执行体内都调用 threadlocal设置了值或去获取值)
public static void main(String[] args) throws InterruptedException { ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); //主线程设置下 最后取出 threadLocal.set(100); Thread thead1 = new Thread(new Runnable() { @Override public void run() {
//它会为null 因为你没在该执行体设置值 System.out.println(Thread.currentThread().getName()+"```:"+threadLocal.get());// null } }, "thead1"); thead1.start(); Thread thead2 = new Thread(new Runnable() { @Override public void run() { threadLocal.set(10) ; System.out.println(Thread.currentThread().getName()+"```:"+threadLocal.get());// null } }, "thead2"); thead2.start(); //这个只是为了等待线程1 2 执行完,让代码按顺序执行 thead1.join(); thead2.join(); //main线程 System.out.println(threadLocal.get());//100
}
原理:表面看上去是在 ThreadLocal 内存了值,其实并不是;threaLocal 只是一个key
我们需要知道,每个线程都有自己的map (ThreadLocal.ThreadLocalMap threadLocals )
这个 map 是以Entry为存储单元的数组,看源码很清晰的;
所以,每个线程都会根据key计算想要存储的这个元素的位置(hash),确定之后,以 Entry 得形式存入,这个Entry也是key value;没错这个key就是threadLocal 和上述计算位置的key一样
取得时候就根据线程自己的threadlocal去取即可,这就是原理
另外ThreadLocal得另一个经典问题就是:内存泄漏;
原因: 当一个生成生命周期很长得时候,加入你此时使用了threadLocal ,然后使用结束了,但是线程没结束,所以ThreadLocalMap依然存在,所以里面的key value 依然存在;这就造成了内存泄漏
对策: Entry 里的key是一个虚引用,但它也仅仅是解决了key得回收问题,
不过呢,value当然也可据此回收,判断key为null,那就对它进行回收,那不就妥了 哈哈
所以,threadlocal 每次set get remove 都会去判断key对key进行回收,所以我们想避免那就记得不用了记得remove即可
疑问:为啥不把value也置为虚引用?
答:key 还没回收,value就没了,那你说咋办?
疑问:key 为虚引用?能不能模拟一下咋就为虚引用了?
你在线程内把threadlocal置为null,且其他地方别用;他就是虚引用了