ThreadLocal其实就是一个工具类,用来操作线程局部变量。
每个线程在jvm的虚拟机里都分配有自己独立的空间,线程之间对于本地的空间是相互隔离的。那么ThreadLocal就应该是该线程空间里本地可以访问的数据了。
首先ThreadLocal有一个静态内部类ThraedLocalMap
这个Map为每个线程复制一个变量的‘拷贝’存储其中。
static class ThreadLocalMap {
//是一个Entry类型的数组
private Entry[] table;
//初始table容量大小为16
private static final int INITIAL_CAPACITY = 16;
//数组元素的个数
private int size = 0;
private int threshold; // Default to 0 阈值
//这个类继承WeakReference 说明这个类是一个弱引用
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}
补充(上面所说的弱引用):
在java中,存在四种引用:强引用、软引用、弱引用、虚引用。
强引用(Strong Reference):通常我们通过new(堆上)来创建一个新对象时返回的引用就是一个强引用,若一个对象通过一系列强引用可到达,它就是强可达的(strongly reachable),那么它就不被回收。
软引用和弱引用(Soft Reference):软引用和弱引用的区别在于,若一个对象是弱引用可达,无论当前内存是否充足它都会被回收,而软引用可达的对象在内存不充足时才会被回收,因此软引用要比弱引用“强”一些。
也就是如果Map中如果是map<Product,Integer>
如果将productA赋值为null,然而map还引用这productA,那么productA过去指向的对象并不会被回收,如果改为弱引用则可以立即被回收掉。
虚引用(Phantom Reference):虚引用是Java中最弱的引用,那么它弱到什么程度呢?它是如此脆弱以至于我们通过虚引用甚至无法获取到被引用的对象,虚引用存在的唯一作用就是当它指向的对象被回收后,虚引用本身会被加入到引用队列(ReferenceQueue)中,用作记录它指向的对象已被回收。
先来看ThreadMap中的set
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;//get长度
int i = key.threadLocalHashCode & (len-1); //通过此线程的HashCode找到对应的table下标(存储位置)
//如果i位置已经存储了对象,那么就往后挪一个位置依次类推,直到找到空的位置,再将对象存放。
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();//接下来查看
}
rehash方法
private void rehash() {
//执行清理工作 ,由于Entry是弱引用,查找key为null的进行GC(遍历Key)
expungeStaleEntries();
// Use lower threshold for doubling to avoid hysteresis
//如果超出了重新扩充并将对象重新计算位置。
if (size >= threshold - threshold / 4)
resize();
}
(2)key.threadLocalHashCode & (len-1)
ThreadLocalMap中Entry[ ]table的大小必须是2的N次方(len = 2^N)
能使哈希码分配均匀
>>> HASH_INCREMENT = 0x61c88647
>>> def magic_hash(n):
... for i in range(n):
... nextHashCode = i * HASH_INCREMENT + HASH_INCREMENT
... print nextHashCode & (n - 1),
... print
...
>>> magic_hash(16)
7 14 5 12 3 10 1 8 15 6 13 4 11 2 9 0
>>> magic_hash(32)
7 14 21 28 3 10 17 24 31 6 13 20 27 2 9 16 23 30 5 12 19 26 1 8 15 22 29 4 11 18 25 0
对于ThreadLocal
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前thread的TheadLocalMap
ThreadLocalMap map = getMap(t);
//如果map不为空
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
通过当前线程,来获得这个线程中的ThreadLocalMap,然后得到实例,
那么对于set,同样,获取当前线程,获得这个线程的ThreadLocalMap,然后,调用这个map的set方法(得到map中的table表,获取长度,得到一个随机分布的索引值,然后要么放入,要么更新值)