Java并发学习之九——使用本地线程变量ThreadLocal
1.什么是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-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

浙公网安备 33010602011771号