ThreadLocal 系列之用简单的方式解释 ThreadLocal

阅读 73

2022-12-22


本文目标:去掉高大上的专有名词,用简单的方式解释 ​​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​ 的成员变量赋了值,一个类实例的成员变量当然归这个类独有了,所以其实没有说的那么高大上。

​​​​​

ThreadLocal 系列之用简单的方式解释 ThreadLocal_线程安全


精彩评论(0)

0 0 举报