ThreadLocal
ThreadLocal可以为不同的线程提供不同变量副本,线程访问threadlocal变量实际上是访问的线程本地的内存中数据副本,线程之间的treadlocal变量互不影响。
基本使用
Example
public class ThreadLocalDemo {
private static ThreadLocal<String> local = new ThreadLocal<>();
public static void main(String[] args) {
new Thread(() -> {
System.out.println("设置线程1的ThreadLocal变量值");
local.set("线程1-value");
String thread2LocalVal = local.get();
System.out.println("[Thread1]:" + thread2LocalVal);
local.remove();
System.out.println("移除线程1的ThreadLocal变量值");
}).start();
new Thread(() -> {
System.out.println("设置线程2的ThreadLocal变量值");
local.set("线程2-value");
String thread2LocalVal = local.get();
System.out.println("[Thread2]:" + thread2LocalVal);
local.remove();
System.out.println("移除线程2的ThreadLocal变量值");
}).start();
}
}
===========output=============
设置线程1的ThreadLocal变量值
设置线程2的ThreadLocal变量值
[Thread2]:线程2-value
[Thread1]:线程1-value
移除线程2的ThreadLocal变量值
移除线程1的ThreadLocal变量值
ThreadLocal的实现原理
首先我们进入到Thread的源码中看看实现逻辑,在Thread中,存在着名为thredLocals和inheritableThreadLocals的属性:
这两个属性都是 ThreadLocal.ThreadLocalMap类型的,具体由ThreadLocal来负责管理和实现。这个map其实ThreadLocal的定制的HashMap。
从源码中可以看出,每个线程这两个变量的默认值为null,那么他们是何时才初始化赋值的呢?不着急,我们通过基本使用的实例代码打断点,进入ThreadLocal的set方法中,一切都会浮出水面。
set()
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
// 获取当前调用该方法的线程
Thread t = Thread.currentThread();
// 获取当前线程的threadLocals变量值
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}
源码面前,了无秘密。set方法显而易见,每个线程的threadLocals变量是在线程第一次调用ThreadLocal变量的set方法的时候才进行初始化,并为之赋值的。
inheritableThreadLocals是使得子线程可以从父线程中得到值的设计的。
每张map中维护着当前线程所关联的多个ThreadLocal变量。
get()
get()方法是从线程的ThreadLocal的map中得到对应的值
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();// 实际上返回的是null,不过会执行类似set的操作,创建map对象
}
private T setInitialValue() {
T value = initialValue();// initialValue():return null
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
if (this instanceof TerminatingThreadLocal) {
TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
}
return value;
}
从源码中,我们可以看到,get方法首先去获得当前调用该threadlocal.get方法的线程,并从该线程中获取到维护着threadlocal的map对象,如果线程的map对象尚未初始化,就执行初始化的操作。并返回null值。
注意
ThreadLocal变量在线程的生命周期中,一旦初始化,是一直存在的,所以可能会造成内存溢出,因此使用完之后,最好调用ThreadLocal的remove方法清除对应线程中的threadLocals的本地变量。