面试题: 谈一谈对 ThreadLocal 的理解

本文详细介绍了Java中的ThreadLocal机制,包括其在实现线程间资源隔离和线程内资源共享的核心原理,以及如何通过set()、get()和remove()方法操作资源。此外,还探讨了ThreadLocalMap中使用弱引用作为key的原因,以防止长期运行的线程导致内存泄漏,并分析了内存释放的时机。最后,通过代码示例展示了ThreadLocal的实际应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目的

学习记录, 面试准备

资源

B站的一个讲高频面试题的一个学习视频

核心结论

线程之间资源隔离, 线程内资源共享.

原理

每个线程内有一个 ThreadLocalMap 类型的成员变量, 用来存储资源对象
① 调用 set(), 就是以 ThreadLocal 自己作为key, 资源对象作为 value, 存入到当前线程的 ThreadLocalMap 中
② 调用 get(), 就是以 ThreadLocal 自己作为key, 到当前线程中查找关联的资源值
③ 调用 remove(), 就是以 ThreadLocal 自己作为key, 移除当前线程关联的资源值

代码演示

/**
 * TestThreadLocal: 线程之间资源隔离, 线程内资源共享
 *
 * @author xiaozhengN 571457082@qq.com
 * @since 2022-11-27 22:12:39
 **/
@Slf4j
public class TestThreadLocal {
    public static void main(String[] args) {
        test1();
        test2();
    }

    /**
     * 多个线程调用, 得到的是自己的Connection对象
     */
    private static void test1() {
        for (int i = 0; i < 5; i++) {
            new Thread(() -> log.info("当前线程: {}, Connection: {}", Thread.currentThread().getName(), Utils.getConnection()), "test1Thread" + (i + 1)).start();
        }
    }

    /**
     * 一个线程内调用, 得到的是同一个Connection对象
     */
    private static void test2() {
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                log.info("当前线程: {}, Connection: {}", Thread.currentThread().getName(), Utils.getConnection());
                log.info("当前线程: {}, Connection: {}", Thread.currentThread().getName(), Utils.getConnection());
            }, "test2Thread" + (i + 1)).start();

        }
    }

    static class Utils {
        // 线程隔离
        private static final ThreadLocal<Connection> threadLocal = new ThreadLocal<>();

        /**
         * 获取连接
         *
         * @return Connection对象
         */
        public static Connection getConnection() {
            Connection connection = threadLocal.get();
            if (connection == null) {
                connection = innerGetConnection();
                threadLocal.set(connection);
            }
            return connection;
        }

        private static Connection innerGetConnection() {
            try {
                return DriverManager.getConnection("jdbc:mysql://localhost:3306/bos?useSSL=false", "root", "root");
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

其他

为什么 ThreadLocalMap 中的key要设计为弱引用

① Thread 可能需要长时间运行(如线程池中的线程), 如果 key 不再使用, 需要 GC 去回收.
② 但 GC 仅仅是释放 key 可内存, 后续还要根据 key 是否为 null 来进一步释放value的内存, 释放时机如下:
2.1 获取 key 发现 null key
2.2 set key时, 会使用启发式扫描, 清除临近的 null key, 启发次数与元素个数, 是否发现null key有关
2.3 remove时(推荐), 因为一般使用 ThreadLocal 时都把它作为静态变量, 因此 GC 无法回收.

参考资源

内存泄漏问题

ThreadLocal内存泄漏

ThreadLocals下标计算

一个特殊的常量 0x61c88647
0x61c88647

源码分析

源码走读

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值