“ThreadLocal:线程局部变量”的版本间差异
跳到导航
跳到搜索
(创建页面,内容为“category:Java == 关于 == <span style="color: blue; font-size: 150%">'''ThreadLocal'''</span> 关键字的概念与作用,在<u>“'''Java 核心技术'''”(卷 I)的“'''并发'''”部分</u><ref name="并发"/>与<u>“'''深入理解 JVM 虚拟机'''”的“'''线程安全'''”部分</u><ref name="线程安全"/>已经了解差不多了。 简单来说:'''ThreadLocal 就是通过定义“线程局部变量”的方式实现“线程…”) |
(→结构关系) |
||
第12行: | 第12行: | ||
2、<big>【<span style="color: blue">'''变量传递'''</span>】</big><span style="color: green">为线程开辟独立的存储空间,用于在'''同一线程的流程中保持上下文'''</span>。 | 2、<big>【<span style="color: blue">'''变量传递'''</span>】</big><span style="color: green">为线程开辟独立的存储空间,用于在'''同一线程的流程中保持上下文'''</span>。 | ||
== | == 源码相关 == | ||
总结: | |||
# <span style="color: blue">'''Thread'''</span> 类: | |||
#* 属性: | |||
#*# '''<syntaxhighlight lang="Java" inline>threadLocals</syntaxhighlight>'''(类型:'''ThreadLocal.ThreadLocalMap''')用于维护“'''线程本地变量'''”。 | |||
# <span style="color: blue">'''ThreadLocal'''</span> 类:<span style="color: green">'''当前线程的 ThreadLocalMap 的访问入口'''</span> | |||
#* 属性: | |||
#*# 每个 ThreadLocal 对象都包含了一个独一无二的 '''<syntaxhighlight lang="Java" inline>threadLocalHashCode</syntaxhighlight>'''; | |||
#* 方法: | |||
#*# '''<syntaxhighlight lang="Java" inline>set(T value)</syntaxhighlight>''':设置“线程本地变量”的值,将调用 ThreadLocalMap 的 '''<syntaxhighlight lang="Java" inline>set(ThreadLocal<?> key, Object value)</syntaxhighlight>'''; | |||
#*# '''<syntaxhighlight lang="Java" inline>T get()</syntaxhighlight>''':获取“线程本地变量”的值,将调用 ThreadLocalMap 的 '''<syntaxhighlight lang="Java" inline>getEntry(ThreadLocal<?> key)</syntaxhighlight>'''; | |||
#*# '''<syntaxhighlight lang="Java" inline>remove()</syntaxhighlight>''':删除此“线程本地变量”,将调用 ThreadLocalMap 的 '''<syntaxhighlight lang="Java" inline>remove(ThreadLocal<?> key)</syntaxhighlight>'''<ref neme="Entry&WeakReference"/>; | |||
#**【<span style="color: red">'''泛型类,所以“线程本地变量”的值可以为各种类型'''</span>】 | |||
# <span style="color: blue">'''ThreadLocalMap'''</span> 类:<span style="color: green">'''存储了一组“K-V”映射'''(其实质是一个 '''Entry 数组''')</span> | |||
#* '''ThreadLocal''' 的内部类; | |||
#* 属性: | |||
#*# '''<syntaxhighlight lang="Java" inline>private Entry[] table;</syntaxhighlight>''':Entry 数组,用于存储“K-V”映射。【key:“ThreadLocal对象”; value:“线程本地变量的值”】 | |||
#* 方法: | |||
#*# '''<syntaxhighlight lang="Java" inline>set(ThreadLocal<?> key, Object value)</syntaxhighlight>''':向 table 中添加/设置 Entry; | |||
#*# '''<syntaxhighlight lang="Java" inline>getEntry(ThreadLocal<?> key)</syntaxhighlight>''':从 table 中获取“ThreadLocal 对象”关联的 Entry; | |||
#*# '''<syntaxhighlight lang="Java" inline>remove(ThreadLocal<?> key)</syntaxhighlight>''':从 table 中移除“ThreadLocal 对象”关联的 Entry; | |||
# <span style="color: blue">'''Entry'''</span> 类:<span style="color: green">'''一个 Entry 就是一个“线程本地变量 -> 线程本地变量的值”的映射'''</span> | |||
#* '''ThreadLocalMap''' 的内部类,继承于:“'''WeakReference'''”<ref neme="Entry&WeakReference"/>; | |||
#* 属性: | |||
#*# '''<syntaxhighlight lang="Java" inline>referent</syntaxhighlight>''':(从 Reference 类中继承而来)保存“ThreadLocal对象” | |||
#*# '''<syntaxhighlight lang="Java" inline>value</syntaxhighlight>''':保存“线程本地变量的值” | |||
=== 源码 === | |||
# <span style="color: blue">'''Thread'''</span>: | |||
#: <syntaxhighlight lang="Java" highlight=""> | |||
package java.lang; | |||
import java.lang.ref.Reference; | |||
import java.lang.ref.ReferenceQueue; | |||
import java.lang.ref.WeakReference; | |||
import ... | |||
public class Thread implements Runnable { | |||
... | |||
/* | |||
* 与此线程相关的 ThreadLocal 值。此映射由【ThreadLocal 类】维护。 | |||
*/ | |||
ThreadLocal.ThreadLocalMap threadLocals = null; | |||
/* | |||
* 与此线程相关的 InheritableThreadLocal 值。此映射由【InheritableThreadLocal 类】维护。 | |||
*/ | |||
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; | |||
... | |||
} | |||
</syntaxhighlight> | |||
#* '''threadLocals''':保存线程的“线程本地变量”。 | |||
#* '''inheritableThreadLocals''':保存线程从父线程继承的“线程本地变量”。 | |||
#** '''InheritableThreadLocal 类''':(继承了 ThreadLocal 类)提供从父线程到子线程的值继承:当创建子线程时,子线程接收父线程具有值的所有可继承的“线程本地变量”的初始值。 | |||
#*** 通常情况下,孩子的值与父母的值相同;但是,通过重写该类中的“childValue”方法,可以使子级的值成为父级的任意函数。 | |||
# <span style="color: blue">'''ThreadLocal'''</span>: | |||
#: <syntaxhighlight lang="Java" highlight=""> | |||
package java.lang; | |||
import java.lang.ref.*; | |||
import java.util.Objects; | |||
import java.util.concurrent.atomic.AtomicInteger; | |||
import java.util.function.Supplier; | |||
/** | |||
* 此类提供【“线程局部变量”】。 | |||
* 这些变量与它们的正常对应变量不同,因为每个访问一个变量的线程(通过其{@code get}或{@code set}方法)都有自己的、独立初始化的变量副本。 | |||
* | |||
* @since 1.2 | |||
*/ | |||
public class ThreadLocal<T> { | |||
/** | |||
* ThreadLocals 依赖于每个线程的 ThreadLocal.ThreadLocalMap(Thread.threadLocals 和 Thread.inheritableThreadLocals)。 | |||
* ThreadLocal.ThreadLocalMap 中: | |||
* ThreadLocal对象充当键,通过 threadLocalHashCode 进行搜索。 | |||
* | |||
* 这是一个自定义哈希代码(仅在ThreadLocalMaps中有用): | |||
* 它可以在相同线程使用连续构造的ThreadLocals的常见情况下消除冲突, | |||
* 而在不太常见的情况下保持良好的行为。 | |||
*/ | |||
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); | |||
} | |||
... | |||
/** | |||
* 【获取:当前线程的“局部变量”(此 ThreadLocal)】 | |||
*/ | |||
public T get() { | |||
// 获取当前线程的 ThreadLocalMap | |||
Thread t = Thread.currentThread(); | |||
ThreadLocalMap map = getMap(t); | |||
/* | |||
* 如果 ThreadLocalMap 不为空: | |||
* 获取“此 ThreadLocal”关联的 Entry 的值 | |||
*/ | |||
if (map != null) { | |||
// 从 ThreadLocalMap 中查找“此 ThreadLocal”关联的 Entry | |||
ThreadLocalMap.Entry e = map.getEntry(this); | |||
if (e != null) { | |||
// 获取 Entry 的值(即“此 ThreadLocal”的值) | |||
@SuppressWarnings("unchecked") | |||
T result = (T)e.value; | |||
return result; | |||
} | |||
} | |||
/* | |||
* 如果 ThreadLocalMap 为空: | |||
* 使用“初始值”来为当前线程创建 ThreadLocalMap | |||
*/ | |||
return setInitialValue(); | |||
} | |||
/** | |||
* 【设置:当前线程的“局部变量”(此 ThreadLocal)】 | |||
*/ | |||
public void set(T value) { | |||
// 获取当前线程的 ThreadLocalMap | |||
Thread t = Thread.currentThread(); | |||
ThreadLocalMap map = getMap(t); | |||
if (map != null) | |||
/* | |||
* 如果“线程的 ThreadLocalMap”不为空: | |||
* 向其中设置 Entry(关联“此 ThreadLocal”、值为 value) | |||
*/ | |||
map.set(this, value); | |||
else | |||
/* | |||
* 如果 ThreadLocalMap 为空: | |||
* 使用 value 来为当前线程创建 ThreadLocalMap | |||
*/ | |||
createMap(t, value); | |||
} | |||
/** | |||
* 【移除:当前线程的“局部变量”(此 ThreadLocal)】 | |||
*/ | |||
public void remove() { | |||
// 获取当前线程的 ThreadLocalMap | |||
ThreadLocalMap m = getMap(Thread.currentThread()); | |||
/* | |||
* 如果 ThreadLocalMap 不为空: | |||
* 移除“此 ThreadLocal”关联的 Entry | |||
*/ | |||
if (m != null) | |||
m.remove(this); | |||
} | |||
ThreadLocalMap getMap(Thread t) { | |||
return t.threadLocals; | |||
} | |||
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); | |||
return value; | |||
} | |||
void createMap(Thread t, T firstValue) { | |||
t.threadLocals = new ThreadLocalMap(this, firstValue); | |||
} | |||
... | |||
/** | |||
* 内部类:【ThreadLocal.ThreadLocalMap】 | |||
* | |||
* ThreadLocalMap是一个定制的哈希映射,仅适用于维护线程本地值。在ThreadLocal类之外不导出任何操作。 | |||
* 该类是包私有的,以允许在Thread类中声明字段。 | |||
* 为了帮助处理非常大且长期的使用,哈希表条目使用WeakReferences作为键。 | |||
* 但是,由于不使用引用队列,只有当表空间开始耗尽时,才保证删除过时的条目。 | |||
*/ | |||
static class ThreadLocalMap { | |||
/** | |||
* 内部类:【ThreadLocal.ThreadLocalMap.Entry】 | |||
* 此哈希映射中的条目扩展 WeakReference,使用其主ref字段作为键(始终是ThreadLocal对象)。 | |||
* | |||
* 注意,空键(即entry.get()==null)意味着不再引用该键,所以可以从表中删除该项。 | |||
*/ | |||
static class Entry extends WeakReference<ThreadLocal<?>> { | |||
/** The value associated with this ThreadLocal. */ | |||
Object value; | |||
Entry(ThreadLocal<?> k, Object v) { | |||
super(k); | |||
value = v; | |||
} | |||
} | |||
// 初始容量 —— 必须是2的幂。 | |||
private static final int INITIAL_CAPACITY = 16; | |||
// 表,根据需要调整大小。table.length 必须始终是2的幂。 | |||
private Entry[] table; | |||
// 表中的条目数。 | |||
private int size = 0; | |||
// 阈值:用于调整大小的下一个size值。 | |||
private int threshold; // Default to 0 | |||
/** | |||
* 设置调整大小的阈值,以最坏情况下保持2/3的负载系数。 | |||
*/ | |||
private void setThreshold(int len) { | |||
threshold = len * 2 / 3; | |||
} | |||
... | |||
/** | |||
* 构造一个新映射:包含最初的(firstKey,firstValue)。 | |||
* | |||
* ThreadLocalMaps是延迟构建的,因此我们只有在至少有一个条目可以放入其中时才创建一个。 | |||
*/ | |||
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); | |||
} | |||
/** | |||
* 构造一个新映射:包括给定父映射中的所有可继承ThreadLocal。 | |||
* | |||
* 仅由createInheritedMap调用。 | |||
*/ | |||
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 关联的 Entry】 | |||
* | |||
* 此方法本身仅处理快速路径: | |||
* 直接命中现有键。否则它将中继到 getEntryAfterMiss。 | |||
* 这是为了最大限度地提高直接命中的性能,部分原因是使该方法易于内联。 | |||
*/ | |||
private Entry getEntry(ThreadLocal<?> key) { | |||
int i = key.threadLocalHashCode & (table.length - 1); | |||
Entry e = table[i]; | |||
if (e != null && e.get() == key) | |||
return e; | |||
else | |||
return getEntryAfterMiss(key, i, e); | |||
} | |||
/** | |||
* 在其直接哈希槽中找不到键时使用的 getEntry 方法的版本。 | |||
*/ | |||
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { | |||
Entry[] tab = table; | |||
int len = tab.length; | |||
while (e != null) { | |||
ThreadLocal<?> k = e.get(); | |||
if (k == key) | |||
return e; | |||
if (k == null) | |||
// 清除过时的条目 | |||
expungeStaleEntry(i); | |||
else | |||
i = nextIndex(i, len); | |||
e = tab[i]; | |||
} | |||
return null; | |||
} | |||
/** | |||
* 【添加与 ThreadLocal 关联的 Entry】 | |||
*/ | |||
private void set(ThreadLocal<?> key, Object value) { | |||
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) { | |||
// 将 set 操作期间遇到的过时项替换为指定 key 的项 | |||
replaceStaleEntry(key, value, i); | |||
return; | |||
} | |||
} | |||
tab[i] = new Entry(key, value); | |||
int sz = ++size; | |||
// 试探性地扫描一些单元格以查找过时的条目。 | |||
if (!cleanSomeSlots(i, sz) && sz >= threshold) | |||
// 重新包装 和/或 调整 table 大小。 | |||
rehash(); | |||
} | |||
/** | |||
* 【移除与 ThreadLocal 关联的 Entry】 | |||
*/ | |||
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; | |||
} | |||
} | |||
} | |||
... | |||
} | |||
} | |||
</syntaxhighlight> | |||
#* ThreadLocal 实例通常是希望将状态与线程关联的类中的“私有静态”('''private static''')字段(例如,用户ID 或 事务ID): | |||
#*: <syntaxhighlight lang="Java" highlight=""> | |||
import java.util.concurrent.atomic.AtomicInteger; | |||
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(); | |||
} | |||
} | |||
</syntaxhighlight> | |||
#*: 如上,类生成每个线程本地的唯一标识符。线程的id在第一次调用{@code ThreadId.get()}时被分配,并且在后续调用时保持不变。 | |||
#* 只要线程处于活动状态且 ThreadLocal 实例可访问,每个线程都保持对其线程本地变量副本的隐式引用;当一个线程离开后,它的所有线程本地实例副本都将被垃圾收集(除非存在对这些副本的其他引用)。 | |||
== 应用过程 == | == 应用过程 == |
2023年1月1日 (日) 03:06的版本
关于
ThreadLocal 关键字的概念与作用,在“Java 核心技术”(卷 I)的“并发”部分[1]与“深入理解 JVM 虚拟机”的“线程安全”部分[2]已经了解差不多了。 简单来说:ThreadLocal 就是通过定义“线程局部变量”的方式实现“线程本地存储”。 其作用(或者说应用场景)[3]: 1、【变量隔离】为变量在每个线程中创建一个副本,避免同一参数在所有线程中传递。 2、【变量传递】为线程开辟独立的存储空间,用于在同一线程的流程中保持上下文。
源码相关
总结:
- Thread 类:
- 属性:
threadLocals
(类型:ThreadLocal.ThreadLocalMap)用于维护“线程本地变量”。
- 属性:
- ThreadLocal 类:当前线程的 ThreadLocalMap 的访问入口
- 属性:
- 每个 ThreadLocal 对象都包含了一个独一无二的
threadLocalHashCode
;
- 每个 ThreadLocal 对象都包含了一个独一无二的
- 方法:
set(T value)
:设置“线程本地变量”的值,将调用 ThreadLocalMap 的set(ThreadLocal<?> key, Object value)
;T get()
:获取“线程本地变量”的值,将调用 ThreadLocalMap 的getEntry(ThreadLocal<?> key)
;remove()
:删除此“线程本地变量”,将调用 ThreadLocalMap 的remove(ThreadLocal<?> key)
引用错误:无效<ref>
标签;无效名称,可能是过多;
- 【泛型类,所以“线程本地变量”的值可以为各种类型】
- 属性:
- ThreadLocalMap 类:存储了一组“K-V”映射(其实质是一个 Entry 数组)
- ThreadLocal 的内部类;
- 属性:
private Entry[] table;
:Entry 数组,用于存储“K-V”映射。【key:“ThreadLocal对象”; value:“线程本地变量的值”】
- 方法:
set(ThreadLocal<?> key, Object value)
:向 table 中添加/设置 Entry;getEntry(ThreadLocal<?> key)
:从 table 中获取“ThreadLocal 对象”关联的 Entry;remove(ThreadLocal<?> key)
:从 table 中移除“ThreadLocal 对象”关联的 Entry;
- Entry 类:一个 Entry 就是一个“线程本地变量 -> 线程本地变量的值”的映射
- ThreadLocalMap 的内部类,继承于:“WeakReference”引用错误:无效
<ref>
标签;无效名称,可能是过多; - 属性:
referent
:(从 Reference 类中继承而来)保存“ThreadLocal对象”value
:保存“线程本地变量的值”
- ThreadLocalMap 的内部类,继承于:“WeakReference”引用错误:无效
源码
- Thread:
package java.lang; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import ... public class Thread implements Runnable { ... /* * 与此线程相关的 ThreadLocal 值。此映射由【ThreadLocal 类】维护。 */ ThreadLocal.ThreadLocalMap threadLocals = null; /* * 与此线程相关的 InheritableThreadLocal 值。此映射由【InheritableThreadLocal 类】维护。 */ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; ... }
- threadLocals:保存线程的“线程本地变量”。
- inheritableThreadLocals:保存线程从父线程继承的“线程本地变量”。
- InheritableThreadLocal 类:(继承了 ThreadLocal 类)提供从父线程到子线程的值继承:当创建子线程时,子线程接收父线程具有值的所有可继承的“线程本地变量”的初始值。
- 通常情况下,孩子的值与父母的值相同;但是,通过重写该类中的“childValue”方法,可以使子级的值成为父级的任意函数。
- InheritableThreadLocal 类:(继承了 ThreadLocal 类)提供从父线程到子线程的值继承:当创建子线程时,子线程接收父线程具有值的所有可继承的“线程本地变量”的初始值。
- ThreadLocal:
package java.lang; import java.lang.ref.*; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; /** * 此类提供【“线程局部变量”】。 * 这些变量与它们的正常对应变量不同,因为每个访问一个变量的线程(通过其{@code get}或{@code set}方法)都有自己的、独立初始化的变量副本。 * * @since 1.2 */ public class ThreadLocal<T> { /** * ThreadLocals 依赖于每个线程的 ThreadLocal.ThreadLocalMap(Thread.threadLocals 和 Thread.inheritableThreadLocals)。 * ThreadLocal.ThreadLocalMap 中: * ThreadLocal对象充当键,通过 threadLocalHashCode 进行搜索。 * * 这是一个自定义哈希代码(仅在ThreadLocalMaps中有用): * 它可以在相同线程使用连续构造的ThreadLocals的常见情况下消除冲突, * 而在不太常见的情况下保持良好的行为。 */ 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); } ... /** * 【获取:当前线程的“局部变量”(此 ThreadLocal)】 */ public T get() { // 获取当前线程的 ThreadLocalMap Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); /* * 如果 ThreadLocalMap 不为空: * 获取“此 ThreadLocal”关联的 Entry 的值 */ if (map != null) { // 从 ThreadLocalMap 中查找“此 ThreadLocal”关联的 Entry ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { // 获取 Entry 的值(即“此 ThreadLocal”的值) @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } /* * 如果 ThreadLocalMap 为空: * 使用“初始值”来为当前线程创建 ThreadLocalMap */ return setInitialValue(); } /** * 【设置:当前线程的“局部变量”(此 ThreadLocal)】 */ public void set(T value) { // 获取当前线程的 ThreadLocalMap Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) /* * 如果“线程的 ThreadLocalMap”不为空: * 向其中设置 Entry(关联“此 ThreadLocal”、值为 value) */ map.set(this, value); else /* * 如果 ThreadLocalMap 为空: * 使用 value 来为当前线程创建 ThreadLocalMap */ createMap(t, value); } /** * 【移除:当前线程的“局部变量”(此 ThreadLocal)】 */ public void remove() { // 获取当前线程的 ThreadLocalMap ThreadLocalMap m = getMap(Thread.currentThread()); /* * 如果 ThreadLocalMap 不为空: * 移除“此 ThreadLocal”关联的 Entry */ if (m != null) m.remove(this); } ThreadLocalMap getMap(Thread t) { return t.threadLocals; } 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); return value; } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } ... /** * 内部类:【ThreadLocal.ThreadLocalMap】 * * ThreadLocalMap是一个定制的哈希映射,仅适用于维护线程本地值。在ThreadLocal类之外不导出任何操作。 * 该类是包私有的,以允许在Thread类中声明字段。 * 为了帮助处理非常大且长期的使用,哈希表条目使用WeakReferences作为键。 * 但是,由于不使用引用队列,只有当表空间开始耗尽时,才保证删除过时的条目。 */ static class ThreadLocalMap { /** * 内部类:【ThreadLocal.ThreadLocalMap.Entry】 * 此哈希映射中的条目扩展 WeakReference,使用其主ref字段作为键(始终是ThreadLocal对象)。 * * 注意,空键(即entry.get()==null)意味着不再引用该键,所以可以从表中删除该项。 */ static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } // 初始容量 —— 必须是2的幂。 private static final int INITIAL_CAPACITY = 16; // 表,根据需要调整大小。table.length 必须始终是2的幂。 private Entry[] table; // 表中的条目数。 private int size = 0; // 阈值:用于调整大小的下一个size值。 private int threshold; // Default to 0 /** * 设置调整大小的阈值,以最坏情况下保持2/3的负载系数。 */ private void setThreshold(int len) { threshold = len * 2 / 3; } ... /** * 构造一个新映射:包含最初的(firstKey,firstValue)。 * * ThreadLocalMaps是延迟构建的,因此我们只有在至少有一个条目可以放入其中时才创建一个。 */ 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); } /** * 构造一个新映射:包括给定父映射中的所有可继承ThreadLocal。 * * 仅由createInheritedMap调用。 */ 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 关联的 Entry】 * * 此方法本身仅处理快速路径: * 直接命中现有键。否则它将中继到 getEntryAfterMiss。 * 这是为了最大限度地提高直接命中的性能,部分原因是使该方法易于内联。 */ private Entry getEntry(ThreadLocal<?> key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); } /** * 在其直接哈希槽中找不到键时使用的 getEntry 方法的版本。 */ private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { Entry[] tab = table; int len = tab.length; while (e != null) { ThreadLocal<?> k = e.get(); if (k == key) return e; if (k == null) // 清除过时的条目 expungeStaleEntry(i); else i = nextIndex(i, len); e = tab[i]; } return null; } /** * 【添加与 ThreadLocal 关联的 Entry】 */ private void set(ThreadLocal<?> key, Object value) { 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) { // 将 set 操作期间遇到的过时项替换为指定 key 的项 replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; // 试探性地扫描一些单元格以查找过时的条目。 if (!cleanSomeSlots(i, sz) && sz >= threshold) // 重新包装 和/或 调整 table 大小。 rehash(); } /** * 【移除与 ThreadLocal 关联的 Entry】 */ 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 实例通常是希望将状态与线程关联的类中的“私有静态”(private static)字段(例如,用户ID 或 事务ID):
import java.util.concurrent.atomic.AtomicInteger; 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(); } }
- 如上,类生成每个线程本地的唯一标识符。线程的id在第一次调用{@code ThreadId.get()}时被分配,并且在后续调用时保持不变。
- 只要线程处于活动状态且 ThreadLocal 实例可访问,每个线程都保持对其线程本地变量副本的隐式引用;当一个线程离开后,它的所有线程本地实例副本都将被垃圾收集(除非存在对这些副本的其他引用)。
应用过程
参考
- ↑ 参考:【核心技术:并发#线程局部变量(ThreadLocal)】
- ↑ 参考:【深入理解JVM:线程安全与锁优化#无同步方案】
- ↑
关于 ThreadLocal 的作用:
- 【变量隔离】:就是用于实现“线程安全”。
- 【变量传递】:参考【Hibernate笔记_4:核心知识#关于“Session_管理方式:绑定到本地线程”】(将业务层获取的 Session 传递到 DAO 层)。