本文目标:去掉高大上的专有名词,用简单的方式解释 ThreadLocal
。
ThreadLocal
是一门很古老的技术,在 JDK 1.2 就出现了,JDK 1.5 之后支持泛型,它协助我们操作线程级别的上下文。关于 ThreadLocal
网上已经有很多资料对其进行分析,经常可以看到这么一句话:“ThreadLocal
提供了线程的局部变量”,不知道为啥都这么说,可能是因为这个类官方注释的第一句话就是:
This class provides thread-local variables.
再看下 provide 这个单词的英文翻译:
- v. 供给;提供;准备;规定;抚养
可以看到,还有“准备”和“规定”的意思。
不好说“ThreadLocal
提供了线程的局部变量”这句话说的对不对,但是个人感觉这个解释某种程度下可能会造成误导,就是好像这个“变量”是 ThreadLocal
提供给每个线程的一样,而且因为这个变量有所谓的不会有“线程安全问题”、“线程独有”等“特性”,就好像这个“变量”很特殊一样,但是实际上这个变量其实没有什么特殊的,就是 Thread
的一个普普通通的成员变量,而 ThreadLocal
就是帮我们操作这个变量的工具,无非就是 Thread
这个类比较特殊,可以随时随地获取实例而已。
分析 ThreadLocal
最主要就是分析所谓的“线程本地变量”是如何进行存储的。
自己实现简易版 ThreadLocal
首先可以猜想一下 ThreadLocal
是如何实现的,我当时看了“ThreadLocal
提供了线程的局部变量”这句话第一个想法就是 ThreadLocal
内部维护了一个 Map
,key
是当前线程,代码如下:
public class MyThreadLocal<T> extends ThreadLocal<T>{
private final Map<Thread,T> HOLDER = new ConcurrentHashMap<>();
@Override
protected T initialValue() {
return null;
}
@Override
public T get() {
Thread t = Thread.currentThread();
T value = HOLDER.get(t);
if (value == null){
return initialValue();
}
return value;
}
@Override
public void set(T value) {
HOLDER.put(Thread.currentThread(),value);
}
@Override
public void remove() {
HOLDER.remove(Thread.currentThread());
}
}
可以简单测试一下:
public class MyThreadLocalTest {
private static final MyThreadLocal<Integer> HOLDER = new MyThreadLocal<Integer>() {
@Override
public Integer initialValue() {
return 0;
}
};
private static final ExecutorService POOL = Executors.newFixedThreadPool(2);
public static void main(String[] args) {
Runnable task = () -> IntStream.range(0, 10).forEach(i -> System.out.println(Thread.currentThread().getName() + ":" + nextInt()));
POOL.submit(task);
POOL.submit(task);
POOL.shutdown();
}
private static Integer nextInt() {
Integer value = HOLDER.get();
value++;
HOLDER.set(value);
return value;
}
}
会发现每个线程的变量都是隔离的,不会出现线程安全问题。这个是猜想的实现,那么看下 JDK 是如何实现的。
ThreadLocal#set
首先看看这个“变量”设置到哪去了。方法其实很简单:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
而 getMap
方法实际上就是获取的 Thread 类中的一个成员变量:
ThreadLocal.ThreadLocalMap threadLocals = null;
最终值就会被设置到这个 threadLocals
成员变量中。而值就是设置到 threadLocals
中。可以看到其实为什么这个“变量”会线程安全、线程独有,并不是 ThreadLocal
给它创造的,而 ThreadLocal
只是为 Thread
的成员变量赋了值,一个类实例的成员变量当然归这个类独有了,所以其实没有说的那么高大上。
欢迎关注公众号
转载:https://blog.csdn.net/Dongguabai/article/details/105168136