一、 ThreadLocal是什么
ThreadLocal,即线程变量,是一个以ThreadLocal对象为键,任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。(注意:一个线程可以存放多个ThreadLocal对象。)
二、 ThreadLocal类的核心方法
ThreadLocal类提供了以下几个核心方法:
- get():获取当前线程的变量值
- set(T value):设置当前线程的变量值
- remove():移除当前线程的变量值
- initialValue():初始化当前线程的变量值
三、 get()方法
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();
}
根据代码可以看出,get()方法首先判断ThreadLocalMap是否存在,如果存在的话,寻找当前ThreadLocal变量所在位置,如果找到,则取出相应的值并返回。那么现在问题来了,第一个问题,什么是ThreadLocalMap,如果这个map不存在怎么办?第二个问题,setInitialValue()是个什么东西。接下来就来看看这两个问题的答案。
四、 ThreadLocalMap和setInitialValue
说白了,ThreadLocalMap就是一个map,是线程私有的,其中存放着各种ThreadLocal变量及其对应的键值。如果当前线程还不存在ThreadLocalMap,则会新建一个初始容量为16的数组,并把第一个键和值放入该map中。代码如下:
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);
}
下面来看看setInitialValue()方法的源码:
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
if (this instanceof TerminatingThreadLocal) {
TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
}
return value;
}
protected T initialValue() {
return null;
}
setInitialValue()方法首先调用initialValue()方法,而initialValue()方法中,直接返回null,即将value初始化为null值。
五、 set()方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}
该方法传入value值作为参数,如果map不为空,则对其进行赋值,如果为空,则调用creatMap方法,而creatMap方法最终调用的还是上文所贴出的代码。
六、remove()方法
private void remove(ThreadLocal<?> key) {
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)]) {
if (e.get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
}
}
根据当前ThreadLocal变量找到相应位置,如果键值相等,则删除,否则进行线性探测,直到找出需要删除的位置。
七、如何解决hash冲突
使用线性探测法,如果计算出的位置中已经有值了,则继续寻找下一个位置。增量为HASH_INCREMENT,是一个固定常数。
private static final int HASH_INCREMENT = 0x61c88647;
private final int threadLocalHashCode = nextHashCode();
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
这篇博客详细解析了ThreadLocal,从其基本概念到核心方法,包括get()、set()、remove(),并探讨了ThreadLocalMap的实现和解决hash冲突的策略,帮助读者深入理解线程局部变量的工作原理。
2381

被折叠的 条评论
为什么被折叠?



