目录
4.3 TTL(TransmittableThreadLocal)
一、ThreadLocal
1.1 什么是 ThreadLocal?
ThreadLocal 提供线程局部变量。这些变量与正常的变量不同,因为每一个线程在访问 ThreadLocal 实例的时候(通过 get 或 set 方法)都有自己的、独立初始化的变量副本。ThreadLocal 实例通常是类的私有静态字段,使用它的目的是希望将状态(例如用户ID或事务ID)与线程关联起来。
1.2 ThreadLocal 的作用
ThreadLocal 线程本地变量,每个线程都拥有一份该变量的独立副本,即使是在多线程环境下,每个线程也只能修改和访问自己的那份副本,从而避免了线程安全问题,实现了线程间的隔离。
案例分析:不同的线程拥有自己的本地变量,卖不同的票数
public class House {
// 使用 AtomicInteger 保证原子性(基于CAS):默认初始值为0
private final AtomicInteger saleCount = new AtomicInteger(0);
/**
* 如果需要使用ThreadLocal,应该是static的
* 通过Lambda表达式初始化,保证每个线程都有自己的副本
*/
private static final ThreadLocal<Integer> saveVolume = ThreadLocal.withInitial(() -> 0);
/**
* 卖房
*/
public void saleHouse() {
saleCount.incrementAndGet(); // 原子操作
saveVolume.set(saveVolume.get() + 1); // 每个线程有自己的副本
}
/**
* 查看销售数
* @return
*/
public int getTotalSales() {
return saleCount.get();
}
public static void main(String[] args) {
// 创建一个共享的House实例
House house = new House();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
int size = new Random().nextInt(5) + 1;
System.out.println(Thread.currentThread().getName() + " 计划销售: " + size);
for (int j = 0; j < size; j++) {
house.saleHouse();
}
// 每个线程都有自己保存的副本,不会相互干扰
System.out.println(Thread.currentThread().getName() + "\t线程本地销售: " + house.saveVolume.get());
}).start();
}
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("总销售数量: " + house.getTotalSales());
}
}
1.3 API
常用方法介绍
1. initialValue():返回线程局部变量的初始值
ThreadLocal<Integer> saveVolume = new ThreadLocal<>(){
@Override
protected Integer initialValue() {
return 0;
}
};
工作中不建议使用该方式,而是通过匿名内部类的方式。
2. withInitial():以匿名内部类的方式设置初始值
与上述的方法的作用相同,但是该匿名内部类的方式在使用上更加简洁。
ThreadLocal<Integer> saveVolume = ThreadLocal.withInitial(()->{
return 0;
});
1.4 开发规范
规范手册规定:【强制】必须回收自定义的 ThreadLocal 变量,尤其是在线程池场景下,线程经常会被复用,如果不清理自定义的 ThreadLocal 变量,可能会影响后续业务逻辑,造成内存泄露的问题。建议合理通过 try-finally块进行回收。
-
因为每个 Thread线程 内有自己的实例副本且该副本只由当前线程自己使用,所以其他线程不能进行访问,那么就不存在多线程间共享的问题
-
我们可以通过统一设置初始值,但是每个线程对这个值的修改都是各自线程互相独立的。
二、ThreadLocal 源码分析
2.1 ThreadLocal 内部实现
Thread、ThreadLocal、ThreadLocalMap 的关系:每个线程Thread内部都存在一个ThreadLocal.ThreadLocalMap。ThreadLocalMap是ThreadLocal的一个静态内部类。此处我们可以通过源码查看:
1. 第一步,点击 Thread 源码:首先我们可以在 Thread 中发现 ThreadLocalMap 变量