Java并发学习之九——使用本地线程变量ThreadLocal
根据JDK文档中的解释:ThreadLocal的作用是提供线程内的局部变量,这种变量在多线程环境下访问时能够保证各个线程里变量的独立性。
从这里可以看出,引入ThreadLocal的初衷是为了提供线程内的局部变量,而不是为了解决共享对象的多线程访问问题。实际上,ThreadLocal根本就不能解决共享对象的多线程访问问题。
2.ThreadLocal实现原理
每个线程中可以持有很多个ThreadLocal对象,这些对象通过hash后存储在Thread的ThreadLocalMap中,其中的Key为ThreadLocal对象,value为该对象在本线程中的一个副本。用图表示如下:
从图中可以看出,ThreadLocal本身并不存储value值,只是作为key在ThreadLocalMap中索引value值
3.ThreadLocal是否会造成内存泄露?
每个Thread含有的ThreadLocalMap中的Key为ThreadLocal变量的弱引用,如果一个ThreadLocal变量没有外部强引用来引用它,那么它在JVM下一次GC的时候会被垃圾回收掉,这时候,Map中就存在了key为NULL的value,这个value无法被访问。如果当前线程再迟迟不结束的话(例如当前线程在一个线程池中),那么value所指向的对象可能永远无法释放,也即不能被回收,造成内存泄露。
ThreadLocalMap的设计者很显然也想到了这个问题,所以其在每一次对ThreadLocalMap的set,get,remove等操作中,都会清除Map中key为null的Entry。因此,ThreadLocal一般是不会存在内存泄露风险的。
但是,将清除NULL对象的工作交给别人,并不是一个明智的选择,所以聪明的你,在Thread中使用完ThreadLocal对象后,一定要记得调用ThreadLocal的remove方法,进行手动清除。
4.示例代码
/*** JDK Version:1.8
*/
public class ThreadLocalTest {
private static final ThreadLocal<Integer> local = ThreadLocal.withInitial(()->0);
public static void main(String[] args) throws InterruptedException {
for(int i = 0; i < 10;i += 3){
new MyThread(i).start();
}
}
static class MyThread extends Thread{
private int end;
public MyThread(int end) {
this.end = end;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " start, local = " + local.get());
for(int i = 0; i <= end;i++){
local.set(local.get() + i); //计算(end+1)*end/2的值
}
System.out.println(Thread.currentThread().getName() + " end, local = " + local.get());
}
}
}
运行结果如下:
Thread-0 start, local = 0
Thread-2 start, local = 0
Thread-1 start, local = 0
Thread-2 end, local = 21
Thread-3 start, local = 0
Thread-0 end, local = 0
Thread-3 end, local = 45
Thread-1 end, local = 6
以上是 Java并发学习之九——使用本地线程变量ThreadLocal 的全部内容, 来源链接: utcz.com/z/394074.html