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

目的

学习记录, 面试准备

资源

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

核心结论

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

原理

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

代码演示

/**
 * TestThreadLocal: 线程之间资源隔离, 线程内资源共享
 *
 * @author xiaozhengN [email protected]
 * @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

源码分析

源码走读

猜你喜欢

转载自blog.csdn.net/xiaozhengN/article/details/128078727
今日推荐