ThreadLocal 原理及例子

目录

通俗解释

三大特点:

经典案例:

和synchronized的区别

应用场景

ThreadLocal 数据结构图示

ThreadLocal 的内存泄漏

那么为什么key要用弱引用呢?

弱引用


黑马程序员Java基础教程由浅入深全面解析threadlocal_哔哩哔哩_bilibili刚接触的学员可以直接看零基础java入门av80585971课程全面,包含:ThreadLocal基本介绍,运用场景,源码分析,常见面试问题等结合源码和画图解构ThreadLocal,更加形象源码分析不仅仅停留在表面,有源码为何这样设计的思考覆盖常见的面试问题: 如TheadLocal和synchronized关键字和内存泄漏方面都有深入的分析https://www.bilibili.com/video/BV1N741127FH?p=10&spm_id_from=pageDriver 由浅入深,全面解析ThreadLocal_LeslieGuGu的博客-CSDN博客_threadlocalhttps://blog.csdn.net/weixin_44050144/article/details/113061884

通俗解释

ThreadLocal 就是每一个线程自己维护的一个map中的key,

比如t1线程有自己的ThreadLocalMap1,里面存放着 ThreadLocal 1, value1

t2线程有自己的ThreadLocalMap2,里面存放着 ThreadLocal 2, value2.

在使用的时候 通过当前线程直接得到自己内部的map,再用键直接取出值即可。

三大特点:

  1. 线程并发: 在多线程并发的场景下
  2. 传递数据: 我们可以通过ThreadLocal在同一线程,不同组件中传递公共变量
  3. 线程隔离: 每个线程的变量都是独立的,不会互相影响,各是各的

经典案例:

下面这个例子中 如果不用threadlocal ,那么会出现 线程1设置了content,线程3把content取出来了,因为并发场景且没有加锁,content只有1份。 使用threadlocal ,设置值的时候,这个content相当于和这个线程关联上了,所以在取值的时候也和这个线程是对应的,所以其实这个content有5份,每个线程都有一份

public class MyDemo1 {

    private static ThreadLocal<String> tl = new ThreadLocal<>();

    private String content;

    private String getContent() {
        return tl.get();
    }

    private void setContent(String content) {
         tl.set(content);
    }

    public static void main(String[] args) {
        MyDemo demo = new MyDemo();
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    demo.setContent(Thread.currentThread().getName() + "的数据");
                    System.out.println("-----------------------");
                    System.out.println(Thread.currentThread().getName() + "--->" + demo.getContent());
                }
            });
            thread.setName("线程" + i);
            thread.start();
        }
    }
}

加锁是可以解决这个问题的,但是在这里我们强调的是线程数据隔离的问题,并不是多线程共享数据的问题, 在这个案例中使用synchronized关键字是不合适的。

和synchronized的区别

应用场景

需要在项目中进行数据传递线程隔离的场景,

在一些特定场景下,ThreadLocal方案有两个突出的优势:

  1. 传递数据 : 保存每个线程绑定的数据,在需要的地方可以直接获取, 避免参数直接传递带来的代码耦合问题

  2. 线程隔离 : 各线程之间的数据相互隔离却又具备并发性,避免同步方式带来的性能损失

ThreadLocal 数据结构图示

​ 在ThreadLocalMap中,也是用Entry来保存K-V结构数据的。不过Entry中的key只能是ThreadLocal对象,这点在构造方法中已经限定死了。

​ 另外,Entry继承WeakReference,也就是key(ThreadLocal)是弱引用,其目的是将ThreadLocal对象的生命周期和线程生命周期解绑。

ThreadLocal 的内存泄漏

ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏。 

 同样假设在业务代码中使用完ThreadLocal ,栈区的threadLocal Ref被回收了。

​ 由于ThreadLocalMap只持有ThreadLocal的弱引用,没有任何强引用指向threadlocal实例, 所以threadlocal就可以顺利被gc回收,此时Entry中的key=null。

​ 但是在没有手动删除这个Entry以及CurrentThread依然运行的前提下,也存在有强引用链 threadRef->currentThread->threadLocalMap->entry -> value ,value不会被回收, 而这块value永远不会被访问到了,导致value内存泄漏。

​ 也就是说,ThreadLocalMap中的key使用了弱引用, 也有可能内存泄漏。
 

那么为什么key要用弱引用呢?

​ 事实上,在 ThreadLocalMap 中的  set/getEntry 方法中,会对key为null(也即是ThreadLocal为null)进行判断,如果为null的话,那么是会对value置为null的。

​ 这就意味着使用完ThreadLocal,CurrentThread依然运行的前提下,就算忘记调用remove方法,弱引用比强引用可以多一层保障:弱引用的ThreadLocal会被回收,对应的value在下一次ThreadLocalMap调用set,get,remove中的任一方法的时候会被清除,从而避免内存泄漏
 

弱引用

【Java虚拟机】弱引用引发的一些思考_Jungle_的博客-CSDN博客

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

trigger333

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值