ThreadLocal
ThreadLocal有两种典型的使用场景:
ThreadLocal用作保存每个线程独享的对象,为每个线程都创建一个副本,这样每个线程都可以修改自己所拥有的副本, 而不会影响其他线程的副本,确保了线程安全。
ThreadLocal用作每个线程内需要独立保存信息,以便供其他方法更方便地获取该信息的场景。每个线程获取到的信息可能都是不一样的,前面执行的方法保存了信息后,后续方法可以通过 ThreadLocal 直接获取到,避免了传参,类似于全局变量的概念。
public T get () {
//获取当前线程
Thread t = Thread . currentThread ();
//获取当前线程内的ThreadLocalMap对象,每一个线程都有一个ThreadLocalMap对象
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 ;
}
}
//如果线程内之前没创建过ThreadLocalMap就创建
return setInitialValue ();
}
ThreadLocalMap getMap ( Thread t ) {
return t . threadLocals ;
}
每个线程都有 ThreadLocalMap 对象,而这个对象的名字就叫 作 threadLocals,初始值为 null
ThreadLocal . ThreadLocalMap threadLocals = null ;
private T setInitialValue () {
T value = initialValue ();
Thread t = Thread . currentThread ();
ThreadLocalMap map = getMap ( t );
if ( map != null ) {
//调用set方法
map . set ( this , value );
} else {
//调用createMap方法
createMap ( t , value );
}
if ( this instanceof TerminatingThreadLocal ) {
TerminatingThreadLocal . register (( TerminatingThreadLocal <?> ) this );
}
return value ;
}
void createMap ( Thread t , T firstValue ) {
t . threadLocals = new ThreadLocalMap ( this , firstValue );
}
private void set ( ThreadLocal <?> key , Object value ) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry [] tab = table ;
int len = tab . length ;
int i = key . threadLocalHashCode & ( len - 1 );
for ( Entry e = tab [ i ] ;
e != null ;
e = tab [ i = nextIndex ( i , len ) ] ) {
ThreadLocal <?> k = e . get ();
if ( k == key ) {
e . value = value ;
return ;
}
if ( k == null ) {
replaceStaleEntry ( key , value , i );
return ;
}
}
tab [ i ] = new Entry ( key , value );
int sz = ++ size ;
if ( ! cleanSomeSlots ( i , sz ) && sz >= threshold )
rehash ();
}
//ThreadLocalMap是ThreadLocal的内部类
ThreadLocalMap ( ThreadLocal <?> firstKey , Object firstValue ) {
table = new Entry [ INITIAL_CAPACITY ] ;
int i = firstKey . threadLocalHashCode & ( INITIAL_CAPACITY - 1 );
table [ i ] = new Entry ( firstKey , firstValue );
size = 1 ;
setThreshold ( INITIAL_CAPACITY );
}
一个线程可能有多个值,A,B,C,D 每一个值对应一个ThreadLocal。所以要使用Map来存储,Key是ThreadLocal对象,value是对应的值。
ThreadLocalMap 既然类似于 Map,所以就和 HashMap 一样,也会有包括 set、get、rehash、resize 等一系列标准操作。但是,虽然思路和 HashMap 是类似的,但是具体实现会有一些不同。
比如其中一个不同点就是,我们知道 HashMap 在面对 hash 冲突的时候,采用的是拉链法。它会先把对象 hash 到一个对应的格 子中,如果有冲突就用链表的形式往下链,如下图所示:
但是 ThreadLocalMap 解决 hash 冲突的方式是不一样的,它采用的是线性探测法。如果发生冲突,并不会用链表的形式往下 链,而是会继续寻找下一个空的格子。这是 ThreadLocalMap 和 HashMap 在处理冲突时不一样的点。
September 28, 2026
December 13, 2021
GitHub