学习记录, 面试准备
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 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);}}}
}
① 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内存泄漏
一个特殊的常量 0x61c88647
0x61c88647
源码走读