Java并发学习之九——使用本地线程变量ThreadLocal

java

    根据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

回到顶部