目录
黑马程序员Java基础教程由浅入深全面解析threadlocal_哔哩哔哩_bilibili刚接触的学员可以直接看零基础java入门av80585971课程全面,包含:ThreadLocal基本介绍,运用场景,源码分析,常见面试问题等结合源码和画图解构ThreadLocal,更加形象源码分析不仅仅停留在表面,有源码为何这样设计的思考覆盖常见的面试问题: 如TheadLocal和synchronized关键字和内存泄漏方面都有深入的分析https://www.bilibili.com/video/BV1N741127FH?p=10&spm_id_from=pageDriver 由浅入深,全面解析ThreadLocal_LeslieGuGu的博客-CSDN博客_threadlocal
https://blog.csdn.net/weixin_44050144/article/details/113061884
通俗解释
ThreadLocal 就是每一个线程自己维护的一个map中的key,
比如t1线程有自己的ThreadLocalMap1,里面存放着
ThreadLocal 1, value1
t2线程有自己的ThreadLocalMap2,里面存放着
ThreadLocal 2, value2.
在使用的时候 通过当前线程直接得到自己内部的map,再用键直接取出值即可。
三大特点:
- 线程并发: 在多线程并发的场景下
- 传递数据: 我们可以通过ThreadLocal在同一线程,不同组件中传递公共变量
- 线程隔离: 每个线程的变量都是独立的,不会互相影响,各是各的
经典案例:
下面这个例子中 如果不用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方案有两个突出的优势:
-
传递数据 : 保存每个线程绑定的数据,在需要的地方可以直接获取, 避免参数直接传递带来的代码耦合问题
-
线程隔离 : 各线程之间的数据相互隔离却又具备并发性,避免同步方式带来的性能损失
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中的任一方法的时候会被清除,从而避免内存泄漏。
弱引用
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/92839.html