ThreadLocal、ThreadLocalMap弱引用key

深入理解ThreadLocal与ThreadLocalMap原理及使用注意事项
本文详细解析ThreadLocal类如何为每个线程提供独立变量拷贝,减少同步需求并提高并发效率。同时,文章指出在使用ThreadLocal时需注意清理对象以避免内存溢出,并强调了ThreadLocalMap的弱引用机制及其对GC的影响。最后,提供了实用的代码示例和关键知识点总结。

ThreadLocal

ThreadLocal类为每一个线程都维护了自己独有的变量拷贝。每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了,那就没有任何必要对这些线程进行同步,它们也能最大限度的由CPU调度,并发执行。并且由于每个线程在访问该变量时,读取和修改的,都是自己独有的那一份变量拷贝,变量被彻底封闭在每个访问的线程中,并发错误出现的可能也完全消除了。对比前一种方案,这是一种以空间来换取线程安全性的策略。

JDK1.6的源码里是通过ThreadLocalMap实现的

JDK1.7中已经没有了ThreadLocalMap,具体实现有空翻下源码


ThreadLocalMap的弱引用会使其数据在GC时被回收掉吗

ThreadLocal源码中有个静态的内部类ThreadLocalMap,里面有个Entry类

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

            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }
其中key是弱引用的



    每个thread中都存在一个map, map的类型是ThreadLocal.ThreadLocalMap. Map中的key为一个threadlocal实例. 这个Map的确使用了弱引用,不过弱引用只是针对key. 每个key都弱引用指向threadlocal. 像上面code中的例子,当把threadlocal实例tl置为null以后,没有任何强引用指向threadlocal实例,所以 threadlocal将会被gc回收. 但是,我们的value却不能回收,因为存在一条从current thread连接过来的强引用. 只有当前thread结束以后,current thread就不会存在栈中,强引用断开,Current Thread, Map, value将全部被GC回收.

从中可以看出,弱引用只存在于key上,所以key会被回收. 而value还存在着强引用.只有thead退出以后,value的强引用链条才会断掉

需要注意的问题

1) ThreadLocal需要注意的问题,每次执行完毕后,要使用remove()方法来清空对象,否则 ThreadLocal 存放大对象后,会出现OMM。

2) ThreadLocal要使用static的 ,在其他地方可以直接用get 和 set方法。

另附上一篇分析非常到位的文章链接:http://www.godiscoder.com/?p=479

### Thread、ThreadLocalThreadLocalMap 的关系 #### 1. **Thread** `Thread` 是 Java 中表示线程的核心类。每个 `Thread` 对象代表一个独立的执行路径,可以并发运行程序中的代码[^3]。 #### 2. **ThreadLocal** `ThreadLocal` 提供了一种机制,使得每个线程都可以拥有自己的变量副本。即使多个线程访问相同的对象实例,它们也不会相互干扰,因为每个线程都有其私有的变量副本[^4]。 - 当调用 `set()` 方法时,`ThreadLocal` 将当前线程作为键,并将值存入到与之关联的 `ThreadLocalMap` 中。 - 调用 `get()` 方法时,则从对应的 `ThreadLocalMap` 中取出属于当前线程的值。 #### 3. **ThreadLocalMap** `ThreadLocalMap` 是一种定制化的哈希表结构,用于存储 `ThreadLocal` 实例与其对应值之间的映射关系。它是一个内部类,定义在 `ThreadLocal` 类中[^5]。 - 每个 `Thread` 都持有一个指向 `ThreadLocal.ThreadLocalMap` 的引用,这个 `ThreadLocalMap` 存储了该线程所使用的全部 `ThreadLocal` 变量及其值。 - 它通过弱引用的方式管理 key(即 `ThreadLocal`),从而避免内存泄漏问题[^6]。 --- ### 使用场景 #### 1. **Thread** 适用于任何需要并行处理的任务。例如,在 Web 应用服务器中创建多个工作线程来响应客户端请求[^7]。 ```java public class MyRunnable implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()); } } // 创建并启动线程 new Thread(new MyRunnable()).start(); ``` #### 2. **ThreadLocal** 当希望在线程间隔离某些共享资源的状态时非常有用。比如数据库连接池、Session 数据保持等场景下,可以通过 `ThreadLocal` 让每个线程都持有自己的一份数据副本而不影响其他线程[^8]。 ```java public class DatabaseConnectionHolder { private static final ThreadLocal<DatabaseConnection> connectionHolder = new ThreadLocal<>(); public static DatabaseConnection getConnection() { return connectionHolder.get(); } public static void setConnection(DatabaseConnection conn) { connectionHolder.set(conn); } } ``` #### 3. **ThreadLocalMap** 通常不需要直接操作 `ThreadLocalMap`,因为它被封装到了 `ThreadLocal` 内部实现细节里。开发者只需关注如何正确使用 `ThreadLocal` 即可间接利用它的功能[^9]。 --- ### 区别总结 | 特性/类别 | Thread | ThreadLocal | ThreadLocalMap | |----------------|----------------------------------|-----------------------------------|------------------------------------| | 主要作用 | 表示单一线程 | 维护线程局部变量 | 存储线程局部变量的实际映射 | | 是否可见于外部 | 公开 | 公开 | 封装隐藏 | | 生命周期 | 整个应用期间存在 | 线程存活期内有效 | 随着线程结束而清理 | --- ### 示例代码展示 下面是一段综合演示三者之间协作的小例子: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadLocalExample { // 定义一个静态的 ThreadLocal 成员变量 private static final ThreadLocal<String> threadLocalValue = new ThreadLocal<>(); public static void main(String[] args) throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(3); for (int i = 0; i < 5; i++) { int taskNumber = i; executor.submit(() -> { try { // 设置特定于本线程的数据 threadLocalValue.set("Task-" + taskNumber); // 执行业务逻辑... System.out.println(Thread.currentThread().getName() + ": Value is " + threadLocalValue.get()); } finally { // 清理 ThreadLocal 值以防潜在泄露风险 threadLocalValue.remove(); } }); } executor.shutdown(); } } ``` 此代码片段展示了如何在一个多线程环境中安全地维护每条线程独有的状态信息。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值