理解ThreadLocal

ThreadLocal有啥用

提供线程内的局部变量。
简单理解就是:如果想要一个变量只是当前线程内使用其他线程访问不到就选择使用ThreadLocal。

怎么使用呢

我们使用程序模拟,多线程情况下,使用ThreadLocal实现线程内数据传递。

声明一个管理ThreadLocal的类

class  MyThreadLocal{
    public static ThreadLocal<Long> threadLocal=new ThreadLocal<>();
    public static void  setVal(long val){
        threadLocal.set(val);
    }
    public static long getVal(){
        return threadLocal.get();
    }
    public static void remove(){
        threadLocal.remove();
    }
}

创建测试类

public class TestThreadLocal {

    public  void method1(){
        new TestThreadLocal2().method2();
}

public class  TestThreadLocal2{
    public  void method2(){
        new TestThreadLocal3().method3();
    }
}

public class  TestThreadLocal3{
    public  void method3(){
        System.out.println(Thread.currentThread().getName()+"===>"+ MyThreadLocal.getVal());
    }
}

main方法测试

启动两个线程,调用TestThreadLocal 类中的方法method1。

public static void main(String[] args) {

        final TestThreadLocal ttl = new TestThreadLocal();
        new Thread(new Runnable() {
            @Override
            public void run() {
                MyThreadLocal.setVal(1000L);
                ttl.method1();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                MyThreadLocal.setVal(2000L);
                ttl.method1();
            }
        }).start();
    }

打印结果:


可以看到两个线程针对相同的ThreadLocal对象设置不同的值相互是独立的,
而且还可以实现单个线程内变量在不同方法之间的传递,减少参数传递。

  • 哪些场景使用了呢
    Hibernate的数据库连接池就是将connection放进threadlocal实现的。

ThreadLocal的原理


如图:

  • Thread类中有一个属性是threadLocals ,这个属性的类型是ThreadLocal中的内部类ThreadLocalMap
ThreadLocal.ThreadLocalMap threadLocals = null;

也就是说每一个Thread类的实例对象都会有一个属于自己的ThreadLocalMap这也是为什么能实现变量线程隔离的原因。

  • ThreadLocalMap是在ThreadLocal中声明的内部类,这个内部类中存在一个Entry内部类,
    它拿当前ThreadLocal对象作为了key,value就是我们指定的。
public class ThreadLocal<T> {
//这里不是全部源码省略了其他方法和属性
static class ThreadLocalMap {
        static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
}
}

ThreadLocal会导致内存泄露吗

答案:会导致内存泄露。

内存泄露的原因:


ThreadLocalMap使用ThreadLocal的弱引用作为key,
如果一个ThreadLocal没有外部强引用来引用它,那么系统 GC 的时候,这个ThreadLocal势必会被回收,
这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,
如果当前线程再迟迟不结束的话(比如:线程池中的核心线程),
这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永远无法回收,造成内存泄漏。

  • ThreadLocalMap的设计中已经考虑到这种情况,也加上了一些防护措施:
    在ThreadLocal的get(),set(),remove()的时候都会清除线程ThreadLocalMap里所有key为null的value

怎么避免内存泄漏呢

每次使用完ThreadLocal,都调用它的remove()方法,清除数据。
在使用线程池的情况下,没有及时清理ThreadLocal,不仅是内存泄漏的问题,更严重的是可能导致业务逻辑出现问题。
所以,使用ThreadLocal就跟加锁完要解锁一样,用完就清理。

理解截图程序

理解来源于这个文章

posted @ 2020-09-21 15:15  王森  阅读(143)  评论(0编辑  收藏  举报