Java ThreadLocal 该类提供了线程局部 (thread-local) 变量

  ThreadLocal,可以理解为线程的局部变量,作用就是为每一个使用该变量的线程都提供一个变量值的副本,每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。

     ThreadLocal是如何做到为每一个线程维护变量的副本的呢?

每个线程中都有一个ThreadLocalMap(Thread.threadLocals),用于存储每一个线程的变量的副本。

ThreadLocalMap使用数组Entry[] table保存ThreadLocal-->Object键值对象,数组保存位置:int i = key.nextHashCode() & (table.length - 1);

  ThreadLocal和Synchonized区别:

  都用于解决多线程并发访问。
  Synchronized用于线程间的数据共享(使变量或代码块在某一时该只能被一个线程访问),是一种以延长访问时间来换取线程安全性的策略;
  而ThreadLocal则用于线程间的数据隔离(为每一个线程都提供了变量的副本),是一种以空间来换取线程安全性的策略。
 
  源码中有以下一段

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);
}

ThreadLocalMap 中 Entry[] table 的大小必须是2的N次方(INITIAL_CAPACITY = 2^N),那INITIAL_CAPACITY - 1的二进制表示就是低位连续的N个1, 那 firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);的值就是 threadLocalHashCode 的低N位。

测试:

private static AtomicInteger nextHashCode = new AtomicInteger();

private static final int HASH_INCREMENT = 0x61c88647;

private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}

public static void main(String[] args) {
for (int j = 0; j < 5; j++) {
int size = 2 << j;
// hash = 0;
int[] indexArray = new int[size];
for (int i = 0; i < size; i++) {
indexArray[i] = nextHashCode() & (size - 1);
}
System.out.println("indexs = "+ Arrays.toString(indexArray));
}
}

结果:

indexs = [0, 1]
indexs = [2, 1, 0, 3]
indexs = [2, 1, 0, 7, 6, 5, 4, 3]
indexs = [2, 9, 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11]
indexs = [18, 25, 0, 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]

没有看到重复的索引值,要哈希表的大小是2的N次方,那么基本上可以保证每次计算出的index值都不会重复。

为什么HashCode不直接用自增的方式(HASH_INCREMENT=1)?

我的理解是,随着不用的 ThreadLocal 变量被回收掉,这种自增的方式的性能会越来越差,因为临近的 slot 为空的可能性很小。而 ThreadLocal 实际所采用的方式,其下标是在跳跃分布,这样即使出现冲突,在临近找到空 slot 的可能性更大一些,性能也会更好。

维护每个线程的ThreadId:

public class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);

// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue () {
return nextId.getAndIncrement();
}
};

// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}

原文地址:https://www.cnblogs.com/shann/p/7107577.html