学习记录, 面试准备
线程之间资源隔离, 线程内资源共享.
每个线程内有一个 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);
}
}
}
}
① 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 无法回收.
一个特殊的常量 0x61c88647
0x61c88647