创建一个 ThreadLocal 变量,那么访问这个变量的每个线程都会有这个变量的一个本地副本。
多线程操作该变量时,实际操作的是自己的副本。

Thread 类中有 threadLocals 和 inheritableThreadLocals,都是 ThreadLocalMap 类型的变量。
默认情况下,每个线程中这两个变量都为 null,当线程第一次调用 ThreadLocal 的 set 或 get 方法才会创建它们。

每个线程的本地变量不是存放在 ThreadLocal 实例里面,而是存放在调用线程的 threadLocals 变量里面。
也就是说,ThreadLocal 类型的本地变量存放在具体的线程内存空间中。

Thread 里面的 threadLocals 为何为 map 结构?
因为每个线程可以关联多个 ThreadLocal 变量

ThreadLocal

  1. package java.lang;
  2. import java.lang.ref.*;
  3. import java.util.Objects;
  4. import java.util.concurrent.atomic.AtomicInteger;
  5. import java.util.function.Supplier;
  6. public class ThreadLocal<T> {
  7. /**ThreadLocal 通过常量 threadLocalHashCode 来标识每一个 ThreadLocal 的唯一性
  8. */
  9. private final int threadLocalHashCode = nextHashCode();
  10. /**
  11. * 下一个 hash code 通过 AtomicInteger 的CAS 操作 保证原子性 初始值为0
  12. */
  13. private static AtomicInteger nextHashCode =
  14. new AtomicInteger();
  15. /**
  16. * Hash增量 ,它可以使hashcode均匀的分布在大小为2的N次方的数组里
  17. */
  18. private static final int HASH_INCREMENT = 0x61c88647;
  19. /**(初始值为0)的基础上每次累加0x61c88647*/
  20. private static int nextHashCode() {
  21. return nextHashCode.getAndAdd(HASH_INCREMENT);
  22. }
  23. protected T initialValue() {
  24. return null;
  25. }
  26. /**
  27. * 支持Lambda表达式,和ThreadLocal重写的initialValue()效果一样。
  28. * ThreadLocal<Integer> seqNum = ThreadLocal.withInitial(() -> 0);
  29. * @since 1.8
  30. */
  31. public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
  32. return new SuppliedThreadLocal<>(supplier);
  33. }
  34. public ThreadLocal() {
  35. }
  36. /**
  37. * 获取当前线程存的值,如果没有则拷贝
  38. */
  39. public T get() {
  40. Thread t = Thread.currentThread();
  41. // 获取当前线程的 threadLocals 变量
  42. ThreadLocalMap map = getMap(t);
  43. if (map != null) {
  44. // 如果线程 threadLocals 有值,则获取 以当前实例对象为key的 value
  45. ThreadLocalMap.Entry e = map.getEntry(this);
  46. // 如果可以找到,则直接返回该 value
  47. if (e != null) {
  48. @SuppressWarnings("unchecked")
  49. T result = (T)e.value;
  50. return result;
  51. }
  52. }
  53. // 找不到就初始化,并返回初始化的值
  54. return setInitialValue();
  55. }
  56. /**
  57. * setInitialValue()调用重写的initialValue()返回新值(如果没有重写initialValue将返回 * 默认值null),
  58. * 并将新值存入当前线程的ThreadLocalMap(如果当前线程没有ThreadLocalMap,会先创建一个)
  59. */
  60. private T setInitialValue() {
  61. T value = initialValue();
  62. Thread t = Thread.currentThread();
  63. ThreadLocalMap map = getMap(t);
  64. if (map != null)
  65. map.set(this, value);
  66. else
  67. createMap(t, value);
  68. return value;
  69. }
  70. public void set(T value) {
  71. // 获取当前线程
  72. Thread t = Thread.currentThread();
  73. // 将当前线程作为 key,去查找对应的线程变量,找到则设置值
  74. ThreadLocalMap map = getMap(t);
  75. if (map != null)
  76. map.set(this, value);
  77. else
  78. // 如果是第一次调,创建线程对应的的 HashMap
  79. createMap(t, value);
  80. }
  81. public void remove() {
  82. // 获取当前线程的ThreadLocalMap
  83. ThreadLocalMap m = getMap(Thread.currentThread());
  84. // map不为空,则移除当前ThreadLocal作为key的键值对。
  85. if (m != null)
  86. m.remove(this);
  87. }
  88. ThreadLocalMap getMap(Thread t) {
  89. // 获取线程自己的 threadLocals
  90. return t.threadLocals;
  91. }
  92. void createMap(Thread t, T firstValue) {
  93. // 创建线程自己的 threadLocals
  94. // threadLocals 的类型就是下面的 ThreadLocalMap
  95. // key 就是当前 ThreadLocal 的实例对象引用,value 就是 set 的值
  96. t.threadLocals = new ThreadLocalMap(this, firstValue);
  97. }
  98. /**
  99. * 在子线程创建的时候会去拷一份父线程的inheritableThreadLocals
  100. */
  101. static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
  102. return new ThreadLocalMap(parentMap);
  103. }
  104. /**
  105. * 方法childValue在子类InheritableThreadLocal中定义
  106. */
  107. T childValue(T parentValue) {
  108. throw new UnsupportedOperationException();
  109. }
  110. /**
  111. * withInitial 返回的
  112. */
  113. static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
  114. private final Supplier<? extends T> supplier;
  115. SuppliedThreadLocal(Supplier<? extends T> supplier) {
  116. this.supplier = Objects.requireNonNull(supplier);
  117. }
  118. @Override
  119. protected T initialValue() {
  120. return supplier.get();
  121. }
  122. }
  123. /**
  124. * 线程放 threadLocal 对象的值的容器
  125. */
  126. static class ThreadLocalMap {
  127. /**
  128. * 之所以用弱引用,是为了解决线程与ThreadLocal之间的强绑定关系
  129. * 会导致如果线程没有被回收,则GC便一直无法回收这部分内容
  130. */
  131. static class Entry extends WeakReference<ThreadLocal<?>> {
  132. /** The value associated with this ThreadLocal. */
  133. Object value;
  134. Entry(ThreadLocal<?> k, Object v) {
  135. super(k);
  136. value = v;
  137. }
  138. }
  139. /**
  140. * 初始容量,必须 2 的 n 次方,为了减少碰撞,能够让保存的元素尽量的分散
  141. */
  142. private static final int INITIAL_CAPACITY = 16;
  143. /**
  144. * 数组容器,容量为 2 的 n 次方
  145. */
  146. private Entry[] table;
  147. /**
  148. * 当前存放的 ThreadLocal 元素数量
  149. */
  150. private int size = 0;
  151. /**
  152. * 下一次扩容的阀值
  153. */
  154. private int threshold; // Default to 0
  155. /**
  156. * 设置下一次阀值为当前的 二分之三
  157. */
  158. private void setThreshold(int len) {
  159. threshold = len * 2 / 3;
  160. }
  161. /**
  162. * 获取下一个索引,超出长度则返回0,所以 entry数组实际上是一个环形结构
  163. */
  164. private static int nextIndex(int i, int len) {
  165. return ((i + 1 < len) ? i + 1 : 0);
  166. }
  167. /**
  168. * 返回上一个索引,如果-1为负数,返回长度-1的索引
  169. */
  170. private static int prevIndex(int i, int len) {
  171. return ((i - 1 >= 0) ? i - 1 : len - 1);
  172. }
  173. /**
  174. * 构造函数,ThreadLocal为key,泛型为value
  175. */
  176. ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
  177. // // 初始化table的大小为16
  178. table = new Entry[INITIAL_CAPACITY];
  179. // 通过hashcode & (长度-1)的位运算,确定键值对的位置
  180. int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
  181. // 创建一个新节点保存在table当中
  182. table[i] = new Entry(firstKey, firstValue);
  183. // 存放元素数量为 1
  184. size = 1;
  185. // 更新下次扩容阀值
  186. setThreshold(INITIAL_CAPACITY);
  187. }
  188. /**
  189. * 构造函数,传入 父线程的 threadLocals 对象
  190. * 这是InheritableThreadLocal提供了了一种父子间数据共享的机制
  191. */
  192. private ThreadLocalMap(ThreadLocalMap parentMap) {
  193. Entry[] parentTable = parentMap.table;
  194. int len = parentTable.length;
  195. setThreshold(len);
  196. table = new Entry[len];
  197. for (int j = 0; j < len; j++) {
  198. Entry e = parentTable[j];
  199. if (e != null) {
  200. @SuppressWarnings("unchecked")
  201. ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
  202. if (key != null) {
  203. Object value = key.childValue(e.value);
  204. Entry c = new Entry(key, value);
  205. int h = key.threadLocalHashCode & (len - 1);
  206. while (table[h] != null)
  207. h = nextIndex(h, len);
  208. table[h] = c;
  209. size++;
  210. }
  211. }
  212. }
  213. }
  214. /**
  215. * 获取指定 ThreadLocal 的索引位置,通过下标索引获取内容
  216. */
  217. private Entry getEntry(ThreadLocal<?> key) {
  218. // 计算下标
  219. int i = key.threadLocalHashCode & (table.length - 1);
  220. Entry e = table[i];
  221. if (e != null && e.get() == key)
  222. return e;
  223. else
  224. // 线性探测法, 找不到的话接着从i位置开始向后遍历
  225. return getEntryAfterMiss(key, i, e);
  226. }
  227. /**
  228. * 线性探测
  229. */
  230. private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
  231. Entry[] tab = table;
  232. int len = tab.length;
  233. // 循环向后遍历,直到找到了其他不相等的 key 停止,返回 null
  234. while (e != null) {
  235. // 获取节点对应的 key
  236. ThreadLocal<?> k = e.get();
  237. // 相等就是找到了,返回
  238. if (k == key)
  239. return e;
  240. // 如果为null,触发一次连续段清理
  241. if (k == null)
  242. expungeStaleEntry(i);
  243. else
  244. // 获取下一个下标接着进行判断
  245. i = nextIndex(i, len);
  246. e = tab[i];
  247. }
  248. return null;
  249. }
  250. /**通过这个方法,我们可以看出该哈希表是用线性探测法来解决冲突的*/
  251. private void set(ThreadLocal<?> key, Object value) {
  252. // 新开一个引用指向table
  253. Entry[] tab = table;
  254. // 获取table的长度
  255. int len = tab.length;
  256. // 获取对应ThreadLocal在table当中的下标
  257. int i = key.threadLocalHashCode & (len-1);
  258. /**
  259. * 从该下标开始循环遍历
  260. * 1、如遇相同key,则直接替换value
  261. * 2、如果该key已经被回收失效,则替换该失效的key
  262. */
  263. for (Entry e = tab[i];
  264. e != null;
  265. e = tab[i = nextIndex(i, len)]) {
  266. ThreadLocal<?> k = e.get();
  267. if (k == key) {
  268. e.value = value;
  269. return;
  270. }
  271. // 如果 k 为 null,则替换当前失效的k所在Entry节点
  272. if (k == null) {
  273. replaceStaleEntry(key, value, i);
  274. return;
  275. }
  276. }
  277. // 找到空的位置,创建Entry对象并插入
  278. tab[i] = new Entry(key, value);
  279. int sz = ++size;
  280. // 如果到达扩容阀值,执行扩容
  281. if (!cleanSomeSlots(i, sz) && sz >= threshold)
  282. rehash();
  283. }
  284. /**
  285. * Remove the entry for key.
  286. */
  287. private void remove(ThreadLocal<?> key) {
  288. Entry[] tab = table;
  289. int len = tab.length;
  290. int i = key.threadLocalHashCode & (len-1);
  291. for (Entry e = tab[i];
  292. e != null;
  293. e = tab[i = nextIndex(i, len)]) {
  294. if (e.get() == key) {
  295. e.clear();
  296. expungeStaleEntry(i);
  297. return;
  298. }
  299. }
  300. }
  301. private void replaceStaleEntry(ThreadLocal<?> key, Object value,
  302. int staleSlot) {
  303. // 新开一个引用指向table
  304. Entry[] tab = table;
  305. int len = tab.length;
  306. Entry e;
  307. // 记录当前失效的节点下标
  308. int slotToExpunge = staleSlot;
  309. /**
  310. * 通过这个for循环的prevIndex(staleSlot, len)可以看出
  311. * 这是由staleSlot下标开始向前扫描
  312. * 查找并记录最前位置value为null的下标
  313. */
  314. for (int i = prevIndex(staleSlot, len);
  315. (e = tab[i]) != null;
  316. i = prevIndex(i, len))
  317. if (e.get() == null)
  318. slotToExpunge = i;
  319. /**
  320. * 通过for循环nextIndex(staleSlot, len)可以看出
  321. * 这是由staleSlot下标开始向后扫描
  322. */
  323. for (int i = nextIndex(staleSlot, len);
  324. (e = tab[i]) != null;
  325. i = nextIndex(i, len)) {
  326. // 获取Entry节点对应的ThreadLocal对象
  327. ThreadLocal<?> k = e.get();
  328. /**
  329. * 如果与新的key对应,直接赋值value
  330. * 则直接替换i与staleSlot两个下标
  331. */
  332. if (k == key) {
  333. e.value = value;
  334. tab[i] = tab[staleSlot];
  335. tab[staleSlot] = e;
  336. // Start expunge at preceding stale entry if it exists
  337. if (slotToExpunge == staleSlot)
  338. slotToExpunge = i;
  339. cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
  340. return;
  341. }
  342. // i之前的节点里,没有value为null的情况
  343. if (k == null && slotToExpunge == staleSlot)
  344. slotToExpunge = i;
  345. }
  346. // If key not found, put new entry in stale slot
  347. tab[staleSlot].value = null;
  348. tab[staleSlot] = new Entry(key, value);
  349. if (slotToExpunge != staleSlot)
  350. cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
  351. }
  352. /**
  353. * 使用的是弱引用,那便有可能在GC的时候就被回收了。所以需要清理
  354. * 如果有很多Entry节点已经被回收了,但是在table数组中还留着位置,不清理就会浪费资源
  355. * 在清理节点的同时,可以将后续非空的Entry节点重新计算下标进行排放,get的时候就能快速定位资源,加快效率。
  356. */
  357. private int expungeStaleEntry(int staleSlot) {
  358. // 新的引用指向table
  359. Entry[] tab = table;
  360. // 获取长度
  361. int len = tab.length;
  362. // 先将传过来的下标位置 设置为null
  363. tab[staleSlot].value = null;
  364. tab[staleSlot] = null;
  365. // 更新当前元素个数 --
  366. size--;
  367. // 遍历删除指定节点所有后续节点当中,ThreadLocal被回收的节点
  368. Entry e;
  369. int i;
  370. for (i = nextIndex(staleSlot, len);
  371. (e = tab[i]) != null;
  372. i = nextIndex(i, len)) {
  373. // 获取entry当中的key
  374. ThreadLocal<?> k = e.get();
  375. // 如果ThreadLocal为null,则将value以及数组下标所在位置设置null,方便GC
  376. if (k == null) {
  377. e.value = null;
  378. tab[i] = null;
  379. size--;
  380. } else {
  381. // 如果后续节点不为空 重新计算key的下标
  382. int h = k.threadLocalHashCode & (len - 1);
  383. // 如果位置不变,则下一个
  384. // 如果位置变了,当前位置设为 null,根据线性探测把 e 设置到新位置
  385. if (h != i) {
  386. tab[i] = null;
  387. while (tab[h] != null)
  388. h = nextIndex(h, len);
  389. tab[h] = e;
  390. }
  391. }
  392. }
  393. return i;
  394. }
  395. private boolean cleanSomeSlots(int i, int n) {
  396. boolean removed = false;
  397. Entry[] tab = table;
  398. int len = tab.length;
  399. do {
  400. i = nextIndex(i, len);
  401. Entry e = tab[i];
  402. if (e != null && e.get() == null) {
  403. n = len;
  404. removed = true;
  405. i = expungeStaleEntry(i);
  406. }
  407. } while ( (n >>>= 1) != 0);
  408. return removed;
  409. }
  410. /**
  411. *重新包装和/或调整桌子的尺寸。首先扫描整个系统
  412. *表删除过时的条目。如果这还不够
  413. *缩小表的大小,将表的大小增加一倍。
  414. */
  415. private void rehash() {
  416. expungeStaleEntries();
  417. // Use lower threshold for doubling to avoid hysteresis
  418. if (size >= threshold - threshold / 4)
  419. resize();
  420. }
  421. /**
  422. * 把容量增加一倍。
  423. */
  424. private void resize() {
  425. Entry[] oldTab = table;
  426. int oldLen = oldTab.length;
  427. int newLen = oldLen * 2;
  428. Entry[] newTab = new Entry[newLen];
  429. int count = 0;
  430. for (int j = 0; j < oldLen; ++j) {
  431. Entry e = oldTab[j];
  432. if (e != null) {
  433. ThreadLocal<?> k = e.get();
  434. if (k == null) {
  435. e.value = null; // Help the GC
  436. } else {
  437. int h = k.threadLocalHashCode & (newLen - 1);
  438. while (newTab[h] != null)
  439. h = nextIndex(h, newLen);
  440. newTab[h] = e;
  441. count++;
  442. }
  443. }
  444. }
  445. setThreshold(newLen);
  446. size = count;
  447. table = newTab;
  448. }
  449. /**
  450. * 删除表中所有过时的条目。
  451. */
  452. private void expungeStaleEntries() {
  453. Entry[] tab = table;
  454. int len = tab.length;
  455. for (int j = 0; j < len; j++) {
  456. Entry e = tab[j];
  457. if (e != null && e.get() == null)
  458. expungeStaleEntry(j);
  459. }
  460. }
  461. }
  462. }

InheritableThreadLocal

�让子线程可以访问在父线程中设置的本地变量。

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    /**
     * 在子线程复制父线程值时调用,子类可以重写该方法,处理子线程复制的初始值
     */
    protected T childValue(T parentValue) {
        return parentValue;
    }

    /**
     *  get也变成了 获取 inheritableThreadLocals
     */
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    /**
     * 第一次 set 时创建的是 inheritableThreadLocals,而不是 threadLocals 了
     */
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

什么情况下需要子线程获取父线程的 threadLocal 变量呢?
比如子线程需要使用存放在 threadLocal 变量中的用户登录信息,
比如中间件需要把同一的 id 追踪的整个调用链路记录下来。

子线程使用父线程的 threadLocal 方法有多种,比如创建线程时传入父线程中的变量,将其复制到子线程。但这都改变了使用习惯。