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() + " start, local = " + local.get());
- }
- }
- }
Thread-2 start, local = 0
Thread-1 start, local = 0
Thread-2 start, local = 21
Thread-3 start, local = 0
Thread-0 start, local = 0
Thread-3 start, local = 45
Thread-1 start, local = 6