知识点1:ThreadLocal是什么?
需要理解线程安全,简单来说造成线程不安全的原因有两个。
-
不想共享的变量被共享了
-
想共享的没及时共享
ThreadLocal解决的是第一个问题,很多情况下我们不希望不同线程之间能互相访问操作对方的变量。例如一个web服务器,多个用户并发访问,用户A有用户A的userId,用户B有用户B的userId。这时候可以使用ThreadLocal保存每个访问线程对应的userId,各读各的,互不干扰。
权威解释
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its{@code get} or {@code set} method) has its own, independently initialized copy of the variable. {@code ThreadLocal} instances are typically private static fields in classes that wish to associate state with a thread (e.g. a user ID or Transaction ID).
Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the {@code ThreadLocal} instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).
不翻译了,记几点解释说明如下:
-
ThreadLocal对象通常是private static fields
-
线程运行结束后可以对该线程的变量副本进行回收,除非该副本有别的引用
-
关键字:副本,线程独享
一个例子,运行两个线程分别设置userId的值,可以看到不同线程互不干扰。
-
public class Test {
-
-
private static ThreadLocal<String> userId = ThreadLocal.withInitial(() -> "init_id");
-
-
public static void main(String[] args) throws InterruptedException {
-
Thread thread1 = new Thread(() -> {
-
try {
-
// 线程1两秒之后获得userid,并且设置userid为id1
-
TimeUnit.SECONDS.sleep(2);
-
System.out.println("initial userId in thread1:" + userId.get());
-
userId.set("id1");
-
System.out.println("thread1 set userId id1");
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
});
-
-
Thread thread2 = new Thread(() -> {
-
try {
-
// 线程二获取初始的userId,然后一秒之后设置为id2,再过两秒之后再次读取userid
-
System.out.println("initial userId in thread2:" + userId.get());
-
TimeUnit.SECONDS.sleep(1);
-
userId.set("id2");
-
System.out.println("thread2 set userId id2");
-
TimeUnit.SECONDS.sleep(2);
-
System.out.println("now userId in thread2:" + userId.get());
-
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
});
-
-
thread1.start();
-
thread2.start();
-
-
// 在main线程等待两个线程执行结束
-
thread1.join();
-
thread2.join();
-
-
}
-
}
-
initial userId in thread2:init_id
-
thread2 set userId id2
-
initial userId in thread1:init_id
-
thread1 set userId id1
-
now userId in thread2:id2
知识点二:ThreadLocal实现原理
最关键代码如下,在任何代码中执行Thread.currentThread(),都可以获取到当前执行这段代码的Thread对象。既然获得了当前的Thread对象了,如果让我们自己实现线程独享变量怎么实现呢?自然而然就会想到在先获取当前Thread对象,然后在当前Thread对象中使用一个容器来存储这些变量,这样每个