【Java核心知识】ThreadLocal相关知识

ThreadLocal

什么是ThreadLocal

ThreadLoacal类可以为每个线程保存一份独有的变量,该变量对于每个线程都是独占的。实现原理为每个Thread类中包含一个ThreadHashMapkey为变量的对应的ThreadLocal对象value为变量的

在日常使用中,我们可以通过set(value)方法设置值,然后通过get()方法获取值,示例代码如下:

我们通过线程池执行了五个线程,每个线程都有一份自己独有的id变量,实现了线程间的数据隔离。

java 复制代码
@Slf4j
public class ThreadLocalTest {
    // ThreadLocal 变量,每个线程都有自己的副本
    private static final ThreadLocal<Integer> id = new ThreadLocal<>();

    @Test
    public void testThreadLocal() {
        // 创建线程池
        ExecutorService executorService = Executors.newCachedThreadPool();

        try {
            for (int i = 0; i < 5; i ++) {
                int finalI = i;
                executorService.execute(() -> {
                    // 设置ThreadLocal 变量    
                    id.set(finalI);
                    // 获取ThreadLocal变量
                    log.info("The id of thread {} is {}", Thread.currentThread().getName(), id.get());
                });
            }
        } finally {
            id.remove();
            executorService.shutdown();
        }
    }
}

传统的HashMap是链地址法,发生碰撞放入桶内;ThreadLocalMap是开放地址法,发生冲突放到后一位

实现原理

那如何实现这种线程间的数据隔离呢?一种容易想到的方法是在Map中进行存储,每个ThreadLocal变量内维护一个Map,其中key为每个线程的IDvalue为对应的值。这样当调用get()方法时,会通过hash算法找到对应线程的值。

jdk也确实是这么做的,只不过存放元素Map的实现不是HashMap,而是ThreadLocalMap,解决冲突的做法是开放寻址法,即出现冲突就往下一个临近位置找,直到找到空位置。

早期的实现中,多个线程会共享一个ThreadLocalMap,这样随着线程数的增加,map就需要扩容,会消耗较多的资源;

后来,每个线程都有自己的一个ThreadLocalMap,各自保存自己的局部变量,key为变量名,value为变量值。

因为局部变量很少,所以一般不需要扩容。

内存泄漏

下面这张图是ThreadLocal对应的引用关系,可以看到,在新的设计下,ThreadLocalMap存在于Thread中,是和Thread的生命周期一致的。

ThreadLocalMapkeyThreadLocal对象,value是对应的值,需要注意的是ThreadHashMapkey会使用弱引用,这主要是为了当ThreadLocalnull后,除了ThreadHashMap持有的弱引用外,没有别的强引用,ThreadLocal对象可以被GC回收

但是这样就导致ThreadHashMap中对应的keynull,其value引用的对象不会被GC回收,从而出现了内存泄漏。

所以在使用完ThreadLocal变量后,尽量使用remove()方法进行清理,这样会把keynull的键值对删除。

再回到上面的示例,上面的示例我们使用final修饰了ThreadLocal,保证其不会被修改,这样做会导致ThreadLocal永远存在强引用,不会被释放,容易发生内存泄露,所以需要我们显示调用remove()方法进行清理。

参考链接

ThreadLocal的内存泄露?什么原因?如何避免?

相关推荐
Mr.Lu ‍2 分钟前
QT调试查看QT内部数据时显示无可用信息,未为 Qt5Cored.dll 加载任何符号
开发语言·qt
qq_452396234 分钟前
第九篇:《Dockerfile 指令精讲(二):WORKDIR、ENV、ARG、EXPOSE》
java·开发语言·docker
JAVA社区6 分钟前
Java高级全套教程(九)—— SpringCloud超详细实战详解
java·开发语言·后端·spring cloud·面试·职场和发展
wyjcxyyy8 分钟前
java反序列化-cc1链
java·c语言·开发语言
山上三树8 分钟前
Python 高频报错速查表(开发通用版)
开发语言·python
garmin Chen9 分钟前
Elasticsearch(1):Elasticsearch核心原理与基础操作总结
java·大数据·笔记·elasticsearch·搜索引擎·全文检索
傻啦嘿哟12 分钟前
解决DNS污染:防止OpenClaw解析API域名到虚假地址
开发语言·php
MY_TEUCK13 分钟前
【MYTRUCK - AI 应用】MetaGPT 0.8.2 安装与排错完整实录(Python 3.10 + 虚拟环境)
开发语言·人工智能·python·ai
Devin~Y13 分钟前
大厂Java面试实录:Spring Boot/Cloud、Kafka、Redis、K8s 可观测性 + RAG/Agent(小Y翻车版)
java·spring boot·redis·spring cloud·kafka·kubernetes·mybatis
林森lsjs17 分钟前
【日耕一题】2. 面向对象 Java 基础:构造方法与 toString
java·开发语言