每个ThreadLocal都有一个threadLocalHashCode,根据该值去选择对应的Entry[]位置,并且 每个都是调用nextHashCode,为什么不直接使用1,2,3,4,5呢?
public class ThreadLocal<T> {
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode = new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
//table的长度必须是2的N次方
private Entry[] table;
//还有其他类和方法。
}
我觉得主要原因,threadLocal会被清除的,threadLocal=null 的虚引用的自动清除(下次set、get时会清除),还是threadLocal.clear()主动清除,会调用到private int expungeStaleEntry(int staleSlot)方法。该方法会size--; 也就是把无效的清除掉 也就不占用Entry[]的空间了,不容易达到阈值了扩容次数就会被减少。
而如果使用1,2,3.....16 就会一直递增,如果2、3、4变为null了,采用什么方案呢?
方案1:不管,继续增加扩容。 缺点占用更大空间。
方案2:也可以size--,但是这样发生碰撞的概率更大,例如1一直有值,因此第17过来,17%16=1一定会发生碰撞。发生碰撞概率更大。而nextHashCode.getAndAdd(0x61c88647) 更随机,更不容易碰撞,及时碰撞,线性探测法解决hash冲突。
测试用例:
private static final int HASH_INCREMENT = 0x61c88647;
public static void main(String[] args) {
magicHash(16); //初始大小16
magicHash(32); //扩容一倍
}
private static void magicHash(int size){
int hashCode = 0;
for(int i=0;i<size;i++){
hashCode = i*HASH_INCREMENT+HASH_INCREMENT;
System.out.print((hashCode & (size-1))+" ");
}
System.out.println();
}
实验结果: