简介
ThreadLocal
叫做线程变量,意思是ThreadLocal
中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。ThreadLocal
为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
ThreadLoal
变量,线程局部变量,同一个 ThreadLocal
所包含的对象,在不同的 Thread
中有不同的副本。
这里有几点需要注意:
因为每个 Thread
内有自己的实例副本,且该副本只能由当前 Thread
使用。这是也是 ThreadLocal
命名的由来。
既然每个 Thread
有自己的实例副本,且其它 Thread
不可访问,那就不存在多线程间共享的问题。
ThreadLocal
提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。ThreadLocal
变量通常被private static
修饰。当一个线程结束时,它所使用的所有 ThreadLocal
相对的实例副本都可被回收。
总的来说,ThreadLocal
适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景
数据结构
Thread
类有一个类型为ThreadLocal.ThreadLocalMap
的实例变量threadLocals
,也就是说每个线程有一个自己的ThreadLocalMap
。
ThreadLocalMap
有自己的独立实现,可以简单地将它key视作ThreadLocal
,value
为放入的值(实际上key并不是ThreadLocal
本身,而是它的一个弱引用)。
线程在往ThreadLocal
里放值的时候,都会往自己的ThreadLocalMap
里存,读也是以ThreadLocal
作为引用,在自己的map里找对应的key,从而实现了线程隔离。
ThreadLocalMap
有点类似HashMap
的结构,只是HashMap
是由数组+链表实现的,而ThreadLocalMap
中并没有链表结构。
我们还要注意Entry
, 它的key是ThreadLocal
k ,继承自WeakReference
, 也就是我们常说的弱引用类型(当没有强引用的时候,GC会回收弱引用)。
示例
public class DemoThreadLocal {
private List<String> messages = Lists.newArrayList();
//ThreadLocal 设置的是一个成员变量,或者外部变量
public static final ThreadLocal<DemoThreadLocal> holder = ThreadLocal.withInitial(DemoThreadLocal::new);
public static void add(String message) {
holder.get().messages.add(message);
}
public static List<String> clear() {
List<String> messages = holder.get().messages;
holder.remove();
System.out.println("size: " + holder.get().messages.size());
return messages;
}
public static void main(String[] args) {
DemoThreadLocal.add("一枝花算不算浪漫");
System.out.println(holder.get().messages);
DemoThreadLocal.clear();
}
}
常用方法
set()
图解
Hash冲突
ThreadLocalMap
不像 hashMap
,有 数组 + 链表 的结构解决hash冲突,ThreadLocalMap
hash冲突 时候,会在原有table[] 数组往后线性查找到空的位置放进去。
扩容机制
1.执行rehash()方法,对过期的key清除
2.第一步执行完后,通过 size >= threshold * 3/4 判断是否要进行扩容
3.扩容的大小是 oldLen * 2
get()
图解
1.ThreadMap
中,key是个弱引用,如果调用set()方法后,线程执行完后,当前线程就没有强引用这个key了,gc时候就key会被直接回收。value不是弱引用,会按正常回收。
2.应用场景MDC
, 分布式日志中的traceId, 请求的时候生成traceId放到ThreadLocal中
, 调用下个链路请求时候,在请求head放入id,下游再取出traceId放入ThreadLocal
关键源码
public class ThreadLocal<T> {
//涉及到 ThreadLocalMap 的哈希值计算
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode = new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;
//放入值, 若果map还不存在,则直接新建一个
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}
//获取值,拿到当前线程的ThreadLocalMap
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();
}
//创建ThreadLocalMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//获取当前线程map
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//ThreadLocalMap 中hash值增长
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
//ThreadLocal 中维护的map对象
static class ThreadLocalMap {
//Entry对象是个弱引用,作为 ThreadLocalMap 的key,没有强引用的时候,会被gc直接回收
static class Entry extends WeakReference<ThreadLocal<?>> {
//value是具体存进map中的value
Object value;
//构造函数
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//ThreadLocalMap 的初始化大小
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
//构造函数
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
//这里会调用 nextHashCode 获取
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
}
}