查看“ThreadLocal:线程局部变量”的源代码
←
ThreadLocal:线程局部变量
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
[[category:Java]] == 关于 == <span style="color: blue; font-size: 150%">'''ThreadLocal'''</span> 关键字的概念与作用,在<u>“'''Java 核心技术'''”(卷 I)的“'''并发'''”部分</u><ref name="并发"/>与<u>“'''深入理解 JVM 虚拟机'''”的“'''线程安全'''”部分</u><ref name="线程安全"/>已经了解差不多了。 简单来说:'''ThreadLocal 就是通过定义“线程局部变量”的方式实现“线程本地存储”'''。 其作用(或者说应用场景)<ref name="作用"></ref>: 1、<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> #* <span style="color: red">'''ThreadLocal 对象通常被定义为“private static”'''</span> #* 属性: #*# 每个 ThreadLocal 对象都包含了一个独一无二的 '''<syntaxhighlight lang="Java" inline>threadLocalHashCode</syntaxhighlight>'''; ——【'''用于计算 ThreadLocal 对象在 ThreadLocalMap.table 中的位置'''】 #* 方法: #*# '''<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>'''<ref name="ThreadLocal.remove"/>:删除此“线程本地变量”,将调用 ThreadLocalMap 的 '''<syntaxhighlight lang="Java" inline>remove(ThreadLocal<?> key)</syntaxhighlight>'''; #**【<span style="color: red">'''泛型类,所以“线程本地变量”的值可以为各种类型'''</span>】 # <span style="color: blue">'''ThreadLocalMap'''</span> 类<ref name="ThreadLocalMap对象的清理"/>:<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 name="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 实例可访问,每个线程都保持对其线程本地变量副本的隐式引用;当一个线程离开后,它的所有线程本地实例副本都将被垃圾收集(除非存在对这些副本的其他引用)。 == 应用过程 == == 参考 == <references> <ref name="并发">参考:【<big>'''[[核心技术:并发#线程局部变量(ThreadLocal)]]'''</big>】</ref> <ref name="线程安全">参考:【<big>'''[[深入理解JVM:线程安全与锁优化#无同步方案]]'''</big>】</ref> <ref name="作用"> 关于 ThreadLocal 的作用: #【变量隔离】:就是用于实现“<span style="color: blue">'''线程安全'''</span>”。 #【变量传递】:参考【'''[[Hibernate笔记_4:核心知识#关于“Session_管理方式:绑定到本地线程”]]'''】(将业务层获取的 Session 传递到 DAO 层)。 </ref> <ref name="Entry&WeakReference"> <span style="color: blue">'''关于<syntaxhighlight lang="Java" inline>Entry extends WeakReference<ThreadLocal<?>></syntaxhighlight>'''</span>: <blockquote> '''WeakReference''' 标志性的特点是: <br/> '''不会影响到被应用对象的 GC 回收行为'''(即,只要对象被除 WeakReference 对象之外所有的对象解除引用后,该对象便可以被 GC 回收), <br/> 只不过在被对象回收之后,WeakReference 实例获取被引用对象时会返回 '''null'''。 </blockquote> : <span style="color: blue">'''为什么 Entry 继承 WeakReference?'''</span> :: <span style="color: green">'''ThreadLocal 对象的 GC 回收'''</span>:如果使用“强引用”,ThreadLocal 对象在使用完毕,将会由于被 Entry 对象“强引用”所引用导致无法被 GC 回收。 </ref> <ref name="ThreadLocalMap对象的清理"> <span style="color: blue">'''ThreadLocalMap 对象的清理'''</span>: : ThreadLocalMap 的“get、set、remove”等方法中,会调用“expungeStaleEntry、replaceStaleEntry、cleanSomeSlots”等方法(根据“<syntaxhighlight lang="Java" inline>if (e != null && e.get() == null)</syntaxhighlight>”判断 Entry 所引用的 ThreadLocal 对象是否还存在)来对 table 进行清理,以及时释放内存。 </ref> <ref name="ThreadLocal.remove"> <span style="color: blue">'''为什么要求“ThreadLocalMap 对象使用完毕后及时调用 remove()?”'''</span>: # 在使用线程池的时候,线程执行完毕放回线程池中,有可能在下一次执行时带入上一次的执行状态。 # 便于对线程的 ThreadLocalMap 对象的 table 及时清理,以释放内存。 </ref> </references>
返回至“
ThreadLocal:线程局部变量
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
大陆简体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
笔记
服务器
数据库
后端
前端
工具
《To do list》
日常
阅读
电影
摄影
其他
Software
Windows
WIKIOE
所有分类
所有页面
侧边栏
站点日志
工具
链入页面
相关更改
特殊页面
页面信息