ThreadLocal使用和原理

简介: 实现机制 1、每个Thread对象内部都维护了一个ThreadLocalMap这样一个ThreadLocal的Map,可以存放若干个ThreadLocal。 /* ThreadLocal values pertaining to this thread.

实现机制

1、每个Thread对象内部都维护了一个ThreadLocalMap这样一个ThreadLocal的Map,可以存放若干个ThreadLocal。

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

2、当我们在调用get()方法的时候,先获取当前线程,然后获取到当前线程的ThreadLocalMap对象,如果非空,那么取出ThreadLocal的value,否则进行初始化,初始化就是将initialValue的值set到ThreadLocal中。

    public T get() {
        //获取当前线程
        Thread t = Thread.currentThread();
        //每个线程拥有一个map,取出来的是线程t的ThreadLocal.ThreadLocalMap对象

ThreadLocalMap map = getMap(t);
if (map != null) {
            //this->ThreadLocal实例是作为map的key来使用的
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        //初始化这个线程的ThreadLocalMap,并且返回null
        return setInitialValue();
    }    

    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

    protected T initialValue() {
        return null;
    }

3、当我们调用set()方法的时候,很常规,就是将值设置进ThreadLocal中。

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            //一个thread.ThreadLocalMap 可以维护多个不同ThreadLocal的值
            map.set(this, value);
        else
            createMap(t, value);
    }    

事实上,从本质来讲,就是每个线程都维护了一个map,而这个map的key就是threadLocal,而值就是我们set的那个值,每次线程在get的时候,都从自己线程的变量map中以ThreadLocal为key来取值,总体来讲,ThreadLocal这个变量的状态根本没有发生变化,他仅仅是充当一个key的角色,另外提供给每一个线程一个初始值。

内存泄露

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。

为什么用弱引用?

key 使用强引用:引用的ThreadLocal的对象被回收了,但是ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏。

key 使用弱引用:引用的ThreadLocal的对象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。value在下一次ThreadLocalMap调用set,get,remove的时候会被清除,否则value内存溢出。

由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key,都会导致内存泄漏,但是使用弱引用可以多一层保障:弱引用ThreadLocal不会内存泄漏,对应的value在下一次ThreadLocalMap调用set,get,remove的时候会被清除。

使用举例

public class TestThreadLocal {

    private static int j = 0;

    public static void main(String[] args) {
        final ThreadLocal<String> tl1 = new ThreadLocal<String>();
        final ThreadLocal<String> tl2 = new ThreadLocal<String>();
        ExecutorService es = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 30; i++) {
            es.execute(new Runnable() {
                @Override
                public void run() {
                    synchronized (TestThreadLocal.class) {
                        tl1.set("tl1"+"-"+j);
                        tl2.set("tl2"+"-"+j);
                        System.out.println("当前线程" + Thread.currentThread()+"-"+ j + "放入data=" + tl1.get());
                        System.out.println("当前线程" + Thread.currentThread()+"-"+ j + "放入data=" + tl2.get());
                        j++;
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("当前线程" + Thread.currentThread() + ":取出data=" + tl1.get());
                    System.out.println("当前线程" + Thread.currentThread() + ":取出data=" + tl2.get());
                }

            });
        }
    }
}

 

目录
相关文章
|
机器人
钉钉markdown
钉钉markdown
1365 0
|
编解码 人工智能 运维
南加大提出全新通用时间序列基础模型TimeDiT!基于扩散模型创新物理约束机制
 【10月更文挑战第10天】南加大提出TimeDiT模型,创新融合扩散模型与Transformer架构,针对真实世界时间序列数据的复杂性,如多分辨率、缺失值等问题,提供高效解决方案。该模型通过新颖的掩码机制和无微调编辑策略,实现多任务处理及物理知识集成,显著提升预测和异常检测的准确性和鲁棒性。
415 3
|
11月前
|
存储 安全 编译器
封装、继承与多态究极详解
本文详细介绍了面向对象编程中的三大核心特性:封装、继承和多态。封装通过隐藏数据和提供接口,确保对象的安全性和一致性;继承通过类之间的“is-a”关系实现代码复用和扩展;多态则允许不同类的对象通过相同的接口执行不同的操作,增强程序的灵活性和可扩展性。文章还探讨了这些特性的底层实现机制,如虚函数表和内存布局,并提供了具体的代码示例。
|
监控 安全 网络安全
WPA2 与 802.1X:有什么区别?
【10月更文挑战第17天】
1073 1
WPA2 与 802.1X:有什么区别?
|
对象存储
阿里云OSS如何对接外链网盘
阿里云OSS如何对接外链网盘
821 1
|
小程序 开发者
微信小程序斗地主完整代码及步骤
微信小程序斗地主完整代码及步骤
1381 0
|
弹性计算 网络协议 数据安全/隐私保护
如何通过公网加入域
如何通过公网加入域
|
监控 测试技术 C++
好玩又实用,阿里巴巴开源混沌工程工具 ChaosBlade
减少故障的最好方法就是让问题经常性的发生。在可控范围或环境下,通过不断重复失败过程,持续提升系统的容错和弹性能力。 那么,实施一次高效的混沌工程实验,需要几步呢? 答案:2 步。 ① 登陆 ChaosBlade ② 下载 release 版本,打造故障演练专属工具 高可用架构是保障服务稳定性的核心。
16613 102
|
消息中间件 前端开发 Java
【Spring Cloud】新闻头条微服务项目:环境搭建及框架准备
主要介绍基于Spring Cloud的文章类微服务项目的功能描述及开发前需要的环境、框架搭建过程。
650 0
【Spring Cloud】新闻头条微服务项目:环境搭建及框架准备
|
存储 监控 安全
无影云桌面的安全设置及等保合规浅谈
简单聊聊无影云桌面的安全设置及等保合规,个人观点,如有缪误,请指正。
4798 3