前言
ThreadLocal类的作用就是对于每个线程,保存一份类的实例。
简单的说实现的话,就是存一个map,key就是当前的线程,value是该变量。
但是实际上的实现还是和我最初想的不一样,这个还要更复杂点。
底层结构
ThreadLocal
ThreadLocal中有三个变量,其中只有一个是成员变量。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16private final int threadLocalHashCode = nextHashCode();
//这个可以看做是ThreadLocal的hashcode(),但是是通过nextHashCode得到的。
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode =
new AtomicInteger();
//这个是每次增加的数,为啥要设为这个奇怪的数呢,我百度了下
//0x61c88647可以使 hashcode 均匀的分布在大小为 2 的 N 次方的数组里
//叫做Fibonacci Hash,这是我们下面进行Entry分配的基础。
private static final int HASH_INCREMENT = 0x61c88647;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
ThreadLocalMap
每个Entry的key存的是ThreadLocal而并不是线程。
和HashMap的分离链表法不同,ThreadLocalMap中处理冲突的方法是开放定址法。
不会产生链表,而是往下找。
这就要求我们散列分的比较均匀。这也是采用Fibonacci的原因。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
31
32
33
34//这个Map的Entry继承了WeakReferenece,也就是弱引用。
//如果这个对象只被弱引用引用,那么它只能活到下一次gc前。
//这个把key作为一个弱引用,就是为了在我们ThreadLocal对象在不被使用后
//能被GC自动清除,避免内存泄漏
//当我们发现key == null的时候,就可以进行清理value的工作。
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//初始的默认大小,参照HashMap的话,这里也必须是2的整数次方。
private static final int INITIAL_CAPACITY = 16;
//底层数组
private Entry[] table;
private int size = 0;
//下一次要扩展的容量大小。
private int threshold;
//构造函数,key是ThreadLocal
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
//这个可以看做是Hash函数。因为是通过的Fibonacci Hashing实现的。
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
//其他的方法我们下面结合ThreadLocal方法看。
}
其中这个map的实例并不是放在ThreadLocal中,而是在Thread中。
1 | public class Thread implements Runnable { |
虽然是在Thread类中,但是还是在ThreadLocal中进行操作的。
Thread类很少对这个类进行操作。
SuppliedThreadLocal
这个类的定义在ThreadLocal中,提供了ThreadLocal的初始值。
正常的我们ThreadLocal如果没有进行set就get的话会得到一个null。
1 | static final class SuppliedThreadLocal<T> extends ThreadLocal<T> { |
这个类重写了initialValue方法,返回我们指定的初始值。
如果我们想得到这个类的实例,ThreadLocal提供了一个静态方法1
2
3public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
return new SuppliedThreadLocal<>(supplier);
}
这个是在1.8中新增的。
基本操作
1 | void createMap(Thread t, T firstValue) { |
方法定义在ThreadLocal中,给线程t创建一个ThreadLocalMap,key为自己。
1 | ThreadLocalMap getMap(Thread t) { |
返回线程t的ThreadLocalMap
1 | public T get() { |
总结
- key为弱引用,避免内存泄漏
- 采用开地址法,运用Fibonacci Hash是hashcode分配均匀。
参考
https://www.cnblogs.com/zhangjk1993/archive/2017/03/29/6641745.html