13.ThreadLocal

本文深入分析了ThreadLocal的工作原理,包括其内部实现机制、源码解读及如何避免内存泄漏问题。ThreadLocal是一种线程本地存储解决方案,每个线程拥有独立的变量副本。文章详细解释了ThreadLocalMap的作用,以及如何通过弱引用解决key和value的内存泄漏。

ThreadLocal类型的变量为线程本地变量,大概的原理就是建立了一个Map,然后key是线程,value是指,通过get方法就能取到每个线程自己的变量副本

1. ThreadLocal使用

2. ThreadLocal源码分析

public class ThreadLocal<T> {
    // 设置属性
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t); // 找到当前线程里的map
        if (map != null)
            map.set(this, value); // 将 键值对 threadLocal的this 和 value 放入到当前线程的map中
        else
            createMap(t, value);
    }
    
    // 获取属性
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t); // 获取map
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this); // 通过 threadLocal的this 获取到value
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    
    // 得到map
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    
    // 创建map
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    
    //将数据从map中移除
    public void remove() {
        ThreadLocalMap m = getMap(Thread.currentThread()); // 通过 threadLocal的this 将键值对移除
        if (m != null) {
            m.remove(this);
        }
    }
}

通过源码分析,真正存储数据的地方是Thread的threadLocals 类型为ThreadLocalMap

  • key: threadLocald对象
  • value: 要存储的值

本来我以为ThreadLocal是一个map,里面存储了Thread和value的键值对,如下表
ThreadLocal

keyvalue
线程1
线程2
线程3

但是真实的存是如下表
Thread

keyvalue
threadLocal1
threadLocal2
threadLocal3

3. ThreadLocal问题

3.1. ThreadLoca置空无法回收的内存泄漏问题

使用ThreadLocal变量时,在使用完毕之后一定要调用remove方法将变量移除,不然会出现线程安全的问题。

就算移除了,然后把ThreadLocal local = null; 置空,但是还有Thread里的map持有local的引用,所以local指向的堆已经对我们不可用了,就产生了内存泄漏

记住是内存泄漏,不是内存溢出 而且泄露的是local对象本身。

但是这个问题已经使用弱引用解决了

    static class ThreadLocalMap {

        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
    }

虽然是Entry继承自WeakReference,但是只有key是若引用类型,这点一定要理解

当用户把local置空,引用断开,在回收的时候Entry里的引用也会断开,至此,local对应的堆已经没有引用指向了,所以会被gc回收掉

3.2. Value无法回收的内存泄漏问题

在上面我们解决了key内存泄漏的问题,但是同样的,就算key被置空,键值对变成了 null=value ,value还是不会被回收

而且value也没有使用弱引用,而是强引用,所以需要用户手动调用remove来移除value

24-Nov-2025 13:42:13.336 涓ラ噸 [RMI TCP Connection(2)-127.0.0.1] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks web搴旂敤绋嬪簭[ssm0u4hl]鍒涘缓浜嗕竴涓猅hreadLocal锛屽叾閿被鍨嬩负[java.lang.ThreadLocal]锛堝�间负[java.lang.ThreadLocal@2de2917f]锛夛紝鍊肩被鍨嬩负[org.apache.ibatis.executor.ErrorContext]锛堝�间负[ ### The error may exist in SQL Mapper Configuration锛夛紝浣嗗湪鍋滄web搴旂敤绋嬪簭鏃舵湭鑳藉皢鍏跺垹闄ゃ�傜嚎绋嬪皢闅忕潃鏃堕棿鐨勬帹绉昏�屾洿鏂帮紝浠ュ皾璇曢伩鍏嶅彲鑳界殑鍐呭瓨娉勬紡 24-Nov-2025 13:42:17.673 淇℃伅 [mysql-cj-abandoned-connection-cleanup] org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading 闈炴硶璁块棶锛氭Web搴旂敤绋嬪簭瀹炰緥宸插仠姝€�傛棤娉曞姞杞絒]銆備负浜嗚皟璇曚互鍙婄粓姝㈠鑷撮潪娉曡闂殑绾跨▼锛屽皢鎶涘嚭浠ヤ笅鍫嗘爤璺熻釜銆� java.lang.IllegalStateException: 闈炴硶璁块棶锛氭Web搴旂敤绋嬪簭瀹炰緥宸插仠姝€�傛棤娉曞姞杞絒]銆備负浜嗚皟璇曚互鍙婄粓姝㈠鑷撮潪娉曡闂殑绾跨▼锛屽皢鎶涘嚭浠ヤ笅鍫嗘爤璺熻釜銆� at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading(WebappClassLoaderBase.java:1370) at org.apache.catalina.loader.WebappClassLoaderBase.getResource(WebappClassLoaderBase.java:994) at com.mysql.cj.jdbc.AbandonedConnectionCleanupThread.checkThreadContextClassLoader(AbandonedConnectionCleanupThread.java:123) at com.mysql.cj.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:90) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at java.base/java.lang.Thread.run(Thread.java:834) 修改错误
11-25
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值