ThreadLocal原理

使用

定义

1
2
3
4
5
6
private static ThreadLocal<String> CHARSET = new ThreadLocal<String>() {
@Override
protected String initialValue() {
return "utf-8";
}
};

或者

1
private static ThreadLocal<String> CHARSET = ThreadLocal.withInitial(() -> "utf-8");

销毁

1
CHARSET.remove();

原理

  • 一个线程Thread包含一个ThreadLocalMap变量
  • 一个ThreadLocal包含一个ThreadLocalMap内部类
  • 一个ThreadLocalMap包含一个Entry内部类
  • 一个ThreadLocalMap的key为ThreadLocal对象
  • 一个ThreadLocalMap的value为Entry的数组

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private T setInitialValue() {
//默认null
T value = initialValue();
//native方法,返回线程对象引用
Thread t = Thread.currentThread();
//默认null
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
//创建ThreadLocalMap
createMap(t, value);
return value;
}

setInitialValue为ThreadLocal方法,负责

  • 初始化线程变量
  • 初始化线程变量值
1
2
3
public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
}

threadLocals属于当前线程,负责

  • 记录线程变量
  • 保持多线程隔离
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class ThreadLocal<T> {
    void createMap(Thread t, T firstValue) {
    //初始化值到map中,this为ThreadLocal对象
    //threadLocals为线程变量
    t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    ThreadLocalMap getMap(Thread t) {
    //获取本线程变量map
    return t.threadLocals;
    }
    }

createMap为ThreadLocal方法,负责创建线程变量map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ThreadLocal<T> {
static class ThreadLocalMap {
//构造方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//创建table,初始容量16
table = new Entry[INITIAL_CAPACITY];
//索引位置
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//填充
table[i] = new Entry(firstKey, firstValue);
//当前大小
size = 1;
//resize 阈值,2/3
//threshold = len * 2 / 3;
setThreshold(INITIAL_CAPACITY);
}
}
}
}

ThreadLocalMap为ThreadLocal静态类,key为ThreadLocal对象,提供

  • 当前线程内,所有变量和值得map
  • 设置索引位置,设置resize阈值

set

ThreadLocal方法

1
2
3
4
5
6
7
8
9
10
public void set(T value) {
//当前线程对象
Thread t = Thread.currentThread();
//获取线程内的变量map
ThreadLocalMap map = getMap(t);
if (map != null)
//赋值
map.set(this, value);
else
createMap(t, value);

赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private void set(ThreadLocal<?> key, Object value) {
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();
//找到key
if (k == key) {
//替换
e.value = value;
return;
}
//k为null,即弱引用的垃圾回收,则清除旧值并替换设置值
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
//不存在则新建
tab[i] = new Entry(key, value);
//当前大小+1
int sz = ++size;
//清除弱引用无用数据并确定是否扩容
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}

  • 负责设置变量值

get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public T get() {
//当前线程
Thread t = Thread.currentThread();
//线程内变量map
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();
}
private Entry getEntry(ThreadLocal<?> key) {
//索引位置
int i = key.threadLocalHashCode & (table.length - 1);
//数组取值
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}

hash和索引计算

firstKey.threadLocalHashCode,获取线程变量hashcode

1
2
3
4
5
private final int threadLocalHashCode = nextHashCode();
private static final int HASH_INCREMENT = 0x61c88647;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}

  • 0x61c88647,斐波那契散列即黄金分割
  • HASH_INCREMENT的0x61c88647对应的十进制为1640531527
  • 0x61c88647与一个神奇的数字产生关系,就是 (Math.sqrt(5) - 1)/2。也就是传说中的黄金比例 0.618(0.618 只是一个粗略值),即0x61c88647 = 2^32 * 黄金分割比。
  • 通过HASH_INCREMENT再借助一定的算法,就可以将哈希码能均匀的分布在2的N次方的数组里,保证了散列表的离散度,从而降低了冲突几率。
  • 用0x61c88647作为魔数累加为每个ThreadLocal分配各自的ID也就是threadLocalHashCode再与2的幂取模,得到的结果分布很均匀。
  • firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1)即求余
------ 本文结束------

本文标题:ThreadLocal原理

文章作者:Perkins

发布时间:2019年11月29日

原始链接:https://perkins4j2.github.io/posts/46263/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。