【JUC】ThreadLocal
文章目录
- 【JUC】ThreadLocal
- 1. 概述
- 2. Thread、ThreadLocal、ThreadLocalMap 关系
- 2.1 Thread 和 ThreadLocal
- 2.2 ThreadLocal 和 ThreadLocalMap
- 2.3 三者之间的关系
1. 概述
ThreadLocal 提供线程局部变量。这些变量与正常的变量不同,因为每一个线程在访问ThreadLocal实例的时候(通过其get或set方法)都有自己的、独立初始化的变量副本。ThreadLocal实例通常是类中的私有静态字段,使用它的目的是希望将状态(例如,用户ID或事务ID)与线程关联起来。
常用API:
ThreadLocal 初始化:
方式1:
ThreadLocal<Integer> threadLocal1 = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue()
{
return 0;
}
};
方式2(推荐):
ThreadLocal<Integer> threadLocal2 = ThreadLocal.withInitial(() -> 0);
使用示例:
需求:5个房屋中介,每个人随机卖出几套房子,统计各自的销售额
代码如下:
class House //资源类
{
int saleCount = 0;
public synchronized void saleHouse() {
++saleCount;
}
ThreadLocal<Integer> saleVolume = ThreadLocal.withInitial(() -> 0);
public void saleVolumeByThreadLocal() {
saleVolume.set(1 + saleVolume.get());
}
}
/**
* 需求1: 5个销售卖房子,集团高层只关心销售总量的准确统计数。
* <p>
* 需求2: 5个销售卖完随机数房子,各自独立销售额度,自己业绩按提成走,分灶吃饭,各个销售自己动手,丰衣足食
*/
public class ThreadLocalDemo {
public static void main(String[] args) throws InterruptedException {
House house = new House();
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
int size = new Random().nextInt(5) + 1;
try {
for (int j = 1; j <= size; j++) {
house.saleHouse();
house.saleVolumeByThreadLocal();
}
System.out.println(Thread.currentThread().getName() + "\t" + "号销售卖出:" + house.saleVolume.get());
} finally {
house.saleVolume.remove();
}
}, String.valueOf(i)).start();
}
;
//暂停毫秒
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t" + "共计卖出多少套: " + house.saleCount);
}
}
运行结果如下:
总结:
- 因为每个 Thread 内有自己的实例副本且该副本只由当前线程自己使用
- 既然其他 Thread 不可访问,那就不存在多线程间共享的问题
- 统一设置初始值,但是每个线程对这个值的修改都是各自线程相互独立的
- 如何才能“不争抢”
- 加入
synchronized
或者 Lock 控制资源的访问顺序 - 人手一份,各自安好。(ThreadLocal)
- 加入
2. Thread、ThreadLocal、ThreadLocalMap 关系
2.1 Thread 和 ThreadLocal
Thread 和 ThreadLocal 之间的关系是,每个线程都拥有自己的 ThreadLocal 副本。也就是说,一个线程可以访问它自己的 ThreadLocal 对象,但无法访问其他线程的 ThreadLocal 对象。
2.2 ThreadLocal 和 ThreadLocalMap
ThreadLocalMap 是 ThreadLocal 的静态内部类,是实现 ThreadLocal 的关键之一,它是与当前线程相关联的一个映射表。当在一个线程中调用 ThreadLocal 的 set() 方法时,实际上是在该线程的 ThreadLocalMap 中存储了一个键值对,其中键是 ThreadLocal 对象本身,值是 set() 方法传入的值。而当调用 get() 方法时,则是从 ThreadLocalMap 中获取对应的值。
2.3 三者之间的关系
ThreadLocalMap 实际上就是一个以 ThreadLocal 实例为key,任意对象为value的 Entry 对象。
当我们为threadLocal变量赋值,实际上就是把当前threadLocal实例为key,值为value的Entry往这个threadLocalMap中存放。
近似的可以理解为:
ThreadLocalMap 从字面上就可以看出这是一个保存 ThreadLocal 对象的map(其实是以ThreadLocal为key),不过是经过了两层包装的ThreadLocal对象:
JVM内部维护了一个线程版的 Map<Thread,T>
(通过ThreadLocal对象的set方法,结果把ThreadLocal对象当作自己的key,放进了ThreadLocalMap中),每个线程要用到这个T的时候,用当前的线程去Map里面获取,通过这样让每个线程都拥有了自己独立的变量,人手一份,竞争条件被彻底消除,在并发模式下是绝对安全的变量。