线程本地变量ThreadLocal

线程本地变量ThreadLocal

线程本地变量只能被本线程访问,不能被其他线程访问。

ThreadLocal相关核心类
  • Thread,有两个属性threadLocals,inheritableThreadLocals,用来保存线程本地变量,threadLocals由ThreadLocal.class维护,inheritableThreadLocals由InheritableThreadLocal.class维护

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /* ThreadLocal values pertaining to this thread. This map is maintained
    * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
    * InheritableThreadLocal values pertaining to this thread. This map is
    * maintained by the InheritableThreadLocal class.
    */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
  • ThreadLocal 保存线程本地变量的工具类

  • ThreadLocal.ThreadLocalMap,保存线程本地变量的容器,K是ThreadLocal实例化对象,值为变量值,保存形式是ThreadLocal.ThreadLocalMap.Entry数组,值在数组的下标由ThreadLocal实例化对象hash后获取

  • ThreadLocal.ThreadLocalMap.Entry,保存线程本地变量K-V的类,K是弱引用

  • WeakReference,弱引用, 当对象仅被弱引用指向,不被其他强引用指向,不论内存空间是否足够,GC都回收。

常用ThreadLocal
  • ThreadLocal 操作线程本地变量
  • InheritableThreadLocal 可以读取父线程以及本线程的变量
常见问题
  • ThreadLocal可以跨线程使用吗
  • ThreadLocal会内存泄漏吗
重要源码阅读
  • inheritableThreadLocals初始化
1
2
// Thread 类: 获取父线程
Thread parent = currentThread();
1
2
3
4
// 如果使用inheritThreadLocals 则根据父线程本地变量创建本地线程本地变量
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// ThreadLocal类: 创建ThreadLocalMap,从父线程把变量循环放入本地线程变量
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];

for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
  • ThreadLocal.ThreadLocalMap的set方法
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
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;
int i = key.threadLocalHashCode & (len-1);

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