ThreadLocal 是线程本地变量,不是线程。

总结与使用

ThreadLocal 如何生效? 实际是Thread中会保存一个ThreadLocalMap ,ThreadLocal 操作都是对当前线程中的ThreadLoacalMap进行操作。

如何使用?
对多线程重复创建对象(对象线程不安全,所有每个线程需要有单独的样本),
如线程池中有10个线程,每一次执行都创建一个SimpleDateFormat(线程不安全,所以每个线程都需要一个新的对象), 线程运行1000次,那么就需要创建1000次SimpleDateFormat对象,而实际每个线程拥有一个对象就不需要再重复创建,也就利用ThreadLocal,将SimpleDateFormat对象保留到线程本地,只需要创建10个对象即可。
Spring JdbcConnection 实现,线程中保留Connetction在本地,能防止connection被其他线程中断,也不需要每个线程创建都建立connection连接。
注意事项:
由于数据保留在线程ThreadLocalMap中,所有如果不清理,会是线程数据不断累增,或者是脏数据带入了下一次线程运行。

1.extends 和 implement

无继承与实现

2.构造方法

空构造

  1. public ThreadLocal() {}

3.属性

threadLocalHashCode: ThreadLocal自定义的HashCode

nextHashCode: AtomicInteger 原子操作,HashCode增长

  1. private final int threadLocalHashCode = nextHashCode();
  2. private static AtomicInteger nextHashCode = new AtomicInteger();

4.方法

nextHashCode()

获取ThreadLocal对象的hashCode值

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

initialValue(): ThreadLocal 初始化方法,返回一个null值

  1. protected T initialValue() {return null; }

withInitial(): 创建线程本地变量,放回的SuppliedThreadLocal重写了initialValue方法,会根据提供的supplier方法来生成初始值

  1. public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
  2. return new SuppliedThreadLocal<>(supplier);
  3. }

get(): 返回当前线程副本的值,如果没有值,就设置并返回初始化值。

  1. public T get() {
  2. Thread t = Thread.currentThread();
  3. //获取线程本地变量对象
  4. ThreadLocalMap map = getMap(t);
  5. if (map != null) {
  6. ThreadLocalMap.Entry e = map.getEntry(this);
  7. if (e != null) {
  8. @SuppressWarnings("unchecked")
  9. T result = (T)e.value;
  10. return result;
  11. }
  12. }
  13. return setInitialValue();
  14. }

getMap(Thread t):获取线程中的ThreadLocalMap(线程本地变量集合)对象值

  1. ThreadLocalMap getMap(Thread t) {
  2. //返回线程类中的ThreadLocal 值
  3. return t.threadLocals;
  4. }

setInitialValue():设置初始化值

  1. private T setInitialValue() {
  2. T value = initialValue();
  3. Thread t = Thread.currentThread();
  4. ThreadLocalMap map = getMap(t);
  5. if (map != null)
  6. map.set(this, value);
  7. else
  8. createMap(t, value);
  9. return value;
  10. }

set(T value): 设置变量值

  1. public void set(T value) {
  2. Thread t = Thread.currentThread();
  3. ThreadLocalMap map = getMap(t);
  4. if (map != null)
  5. map.set(this, value);
  6. else
  7. createMap(t, value);
  8. }

remove(): 移除变量值

  1. public void remove() {
  2. ThreadLocalMap m = getMap(Thread.currentThread());
  3. if (m != null)
  4. m.remove(this);
  5. }

createMap(Thread t, T value): 如果线程本地变量集合为null 那么创建一个线程本地变量集合

  1. void createMap(Thread t, T firstValue) {
  2. t.threadLocals = new ThreadLocalMap(this, firstValue);
  3. }

createInheritedMap(ThreadLocalMap ): 这个方法提供给线程类使用,为了让线程能继承父线程的本地变量。

详细可查看线程类的init方法:Thread.init() 代码第45行

  1. static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
  2. return new ThreadLocalMap(parentMap);
  3. }

childValue(T parentValue):

InheritableThreadLocal 类重写了此方法,返回 parentValue

  1. T childValue(T parentValue) {
  2. throw new UnsupportedOperationException();
  3. }

5.内部类

静态内部类

ThreadLocalMap: 存储结构类

  1. static class ThreadLocalMap {
  2. //ThreadLocal 作为弱引用键,如果ThreadLocal =null,也就意味着不在引用该键
  3. static class Entry extends WeakReference<ThreadLocal<?>> {
  4. Object value;
  5. Entry(ThreadLocal<?> k, Object v) {
  6. super(k);
  7. value = v;
  8. }
  9. }
  10. //初始化大小,必须是2的幂 为什么? 计算Entry数组下标时用,见 TheadLocalMap(ThreadLocal<?> firstKey, Object firstValue)方法
  11. private static final int INITIAL_CAPACITY = 16;
  12. //数据存储 table.length 必须是2的幂
  13. private Entry[] table;
  14. //table的大小
  15. private int size = 0;
  16. //下一次扩容的大小 长度的 0.5
  17. private int threshold;
  18. private void setThreshold(int len) {
  19. threshold = len * 2 / 3;
  20. }
  21. //数组的下一个位置索引,超过长度回到初始位置
  22. private static int nextIndex(int i, int len) {
  23. return ((i + 1 < len) ? i + 1 : 0);
  24. }
  25. //数组的前一个位置索引
  26. private static int prevIndex(int i, int len) {
  27. return ((i - 1 >= 0) ? i - 1 : len - 1);
  28. }
  29. //构造方法
  30. ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
  31. //初始化数组大小
  32. table = new Entry[INITIAL_CAPACITY];
  33. //下标是hashCode 与上 (INITIAL_CAACITY -1) 2的幂是 100000... , 10000.... -1 = 11111......
  34. int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
  35. //设置初始值
  36. table[i] = new Entry(firstKey, firstValue);
  37. size = 1;
  38. //设置下一次扩容的大小
  39. setThreshold(INITIAL_CAPACITY);
  40. }
  41. //私有构造 复制一个新的ThreadLocalMap ,唯一调用的方法为 createInheritedMap(ThreadLocalMap parentMap);
  42. private ThreadLocalMap(ThreadLocalMap parentMap) {
  43. Entry[] parentTable = parentMap.table;
  44. int len = parentTable.length;
  45. setThreshold(len);
  46. table = new Entry[len];
  47. for (int j = 0; j < len; j++) {
  48. Entry e = parentTable[j];
  49. if (e != null) {
  50. @SuppressWarnings("unchecked")
  51. ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
  52. if (key != null) {
  53. Object value = key.childValue(e.value);
  54. Entry c = new Entry(key, value);
  55. int h = key.threadLocalHashCode & (len - 1);
  56. while (table[h] != null)
  57. h = nextIndex(h, len);
  58. table[h] = c;
  59. size++;
  60. }
  61. }
  62. }
  63. }
  64. //查询数组里的对象
  65. private Entry getEntry(ThreadLocal<?> key) {
  66. //获取数组下标
  67. int i = key.threadLocalHashCode & (table.length - 1);
  68. //如果查询到了,就返回e ,
  69. //未查询到,调用getEntryAfterMiss,遍历Entry数组进行查询(从位置i开始查询,因为set时,是从下标i向后寻找空位set)
  70. Entry e = table[i];
  71. if (e != null && e.get() == key)
  72. return e;
  73. else
  74. return getEntryAfterMiss(key, i, e);
  75. }
  76. //遍历数组查询
  77. private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
  78. Entry[] tab = table;
  79. int len = tab.length;
  80. while (e != null) {
  81. ThreadLocal<?> k = e.get();
  82. if (k == key)
  83. return e;
  84. if (k == null)
  85. //如果key为空,那么删除这个数据,并且重新对数组进行散列计算
  86. expungeStaleEntry(i);
  87. else
  88. i = nextIndex(i, len);
  89. e = tab[i];
  90. }
  91. return null;
  92. }
  93. //set 属性值 1.set值 2.扩容
  94. private void set(ThreadLocal<?> key, Object value) {
  95. Entry[] tab = table;
  96. int len = tab.length;
  97. int i = key.threadLocalHashCode & (len-1);
  98. //从下标i开始向后寻址, 直到找到空位,nextIndex是环形循环,到达数组终点时会回到起点继续循环。
  99. for (Entry e = tab[i];e != null; e = tab[i = nextIndex(i, len)]) {
  100. ThreadLocal<?> k = e.get();
  101. if (k == key) {
  102. e.value = value;
  103. return;
  104. }
  105. //如果当前位置的key已经不在使用,那么进行替换 (注意,后续位置可能存在Value相同的key,所以replaceStatleEntry还需要遍历后续位置)
  106. if (k == null) {
  107. replaceStaleEntry(key, value, i);
  108. return;
  109. }
  110. }
  111. tab[i] = new Entry(key, value);
  112. int sz = ++size;
  113. if (!cleanSomeSlots(i, sz) && sz >= threshold)
  114. rehash();
  115. }
  116. private void remove(ThreadLocal<?> key) {
  117. Entry[] tab = table;
  118. int len = tab.length;
  119. int i = key.threadLocalHashCode & (len-1);
  120. for (Entry e = tab[i];
  121. e != null;
  122. e = tab[i = nextIndex(i, len)]) {
  123. if (e.get() == key) {
  124. e.clear();
  125. expungeStaleEntry(i);
  126. return;
  127. }
  128. }
  129. }
  130. //新的Entry 替换已经弃用的Entry
  131. private void replaceStaleEntry(ThreadLocal<?> key, Object value,
  132. int staleSlot) {
  133. Entry[] tab = table;
  134. int len = tab.length;
  135. Entry e;
  136. int slotToExpunge = staleSlot;
  137. //向上查找 找到其他需要清除 Entry
  138. //这样 slotToExpunge 代表清除的终点, staleSlot代表清除的起点, 相等时,只清除staleSlot的Entry
  139. for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null;i = prevIndex(i, len))
  140. if (e.get() == null) slotToExpunge = i;
  141. //遍历Entry数组,看是否能找到与当前ThreadLocalKey相等的对象,如果有,那么进行交换,将Entry交换到staleSlot位置,并替换value
  142. for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null;i = nextIndex(i, len)) {
  143. ThreadLocal<?> k = e.get();
  144. if (k == key) {
  145. e.value = value;
  146. tab[i] = tab[staleSlot];
  147. tab[staleSlot] = e;
  148. if (slotToExpunge == staleSlot)
  149. slotToExpunge = i;
  150. //清除数组中弃用的key
  151. cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
  152. return;
  153. }
  154. //在处理过程中如果有新的key失效,并且在向上查找时没有找到,那么更换slotToExpunge的位置
  155. if (k == null && slotToExpunge == staleSlot)
  156. slotToExpunge = i;
  157. }
  158. //如果没有在数组中找到与插入TheadLoaclKey相等的,那么直接插入到staleSlot
  159. tab[staleSlot].value = null;
  160. tab[staleSlot] = new Entry(key, value);
  161. //如果需要替换的位置与向上查找时的位置一样,而这个时候staleSlot的位置已经替换了新值,所有就不需要清理数组了
  162. //如果不一样,那么还是需要清理数组
  163. if (slotToExpunge != staleSlot)
  164. cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
  165. }
  166. //重新散列数组,并且清除弃用的Entry
  167. private int expungeStaleEntry(int staleSlot) {
  168. Entry[] tab = table;
  169. int len = tab.length;
  170. tab[staleSlot].value = null;
  171. tab[staleSlot] = null;
  172. size--;
  173. Entry e;
  174. int i;
  175. //从下标staleSlot开始 向后遍历 直到碰到 数组下标对应的Enrty为空
  176. for (i = nextIndex(staleSlot, len); (e = tab[i]) != null;i = nextIndex(i, len)) {
  177. ThreadLocal<?> k = e.get();
  178. //key 为null ,那么清除这个数据,否则就移动到离hash计算的位置最近的空槽处
  179. if (k == null) {
  180. e.value = null; tab[i] = null; size--;
  181. } else {
  182. int h = k.threadLocalHashCode & (len - 1);
  183. if (h != i) {
  184. tab[i] = null;
  185. while (tab[h] != null) h = nextIndex(h, len);
  186. tab[h] = e;
  187. }
  188. }
  189. }
  190. //这时候 i是 staleSlot 遍历的第一个空槽位置
  191. return i;
  192. }
  193. //清理 n>>>=1 ,表示循环次数为2的幂,也就是len是2的多少次方, 如果len=4 那么循环2次
  194. private boolean cleanSomeSlots(int i, int n) {
  195. boolean removed = false;
  196. Entry[] tab = table;
  197. int len = tab.length;
  198. do {
  199. i = nextIndex(i, len);
  200. Entry e = tab[i];
  201. if (e != null && e.get() == null) {
  202. //如果找到了新的需要废弃的Entry,那么先清理,然后n重置,继续循环
  203. n = len; removed = true; i = expungeStaleEntry(i);
  204. }
  205. } while ( (n >>>= 1) != 0);
  206. return removed;
  207. }
  208. //清理数组所有弃用的Entry,刷新数组大小,重新hash
  209. private void rehash() {
  210. expungeStaleEntries();
  211. if (size >= threshold - threshold / 4)
  212. resize();
  213. }
  214. //刷新数组大小,重新hash
  215. private void resize() {
  216. Entry[] oldTab = table;
  217. int oldLen = oldTab.length;
  218. int newLen = oldLen * 2;
  219. //扩容
  220. Entry[] newTab = new Entry[newLen];
  221. int count = 0;
  222. //重新hash到新的数组中
  223. for (int j = 0; j < oldLen; ++j) {
  224. Entry e = oldTab[j];
  225. if (e != null) {
  226. ThreadLocal<?> k = e.get();
  227. if (k == null) {
  228. e.value = null; // Help the GC
  229. } else {
  230. int h = k.threadLocalHashCode & (newLen - 1);
  231. while (newTab[h] != null)
  232. h = nextIndex(h, newLen);
  233. newTab[h] = e;
  234. count++;
  235. }
  236. }
  237. }
  238. //重置扩容的阈值
  239. setThreshold(newLen);
  240. //数组size ,数据的总数
  241. size = count;
  242. table = newTab;
  243. }
  244. //清除表中所有的弃用Entry
  245. private void expungeStaleEntries() {
  246. Entry[] tab = table;
  247. int len = tab.length;
  248. for (int j = 0; j < len; j++) {
  249. Entry e = tab[j];
  250. if (e != null && e.get() == null)
  251. expungeStaleEntry(j);
  252. }
  253. }
  254. }

SuppliedThreadLocal:

静态不可被继承类,继承了ThreadLocal类, 重写了initialValue()发放,在调用SuppliedThreadLocal类时,会根据Supplier.get(),拿到初始值。

  1. static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
  2. private final Supplier<? extends T> supplier;
  3. SuppliedThreadLocal(Supplier<? extends T> supplier) {
  4. this.supplier = Objects.requireNonNull(supplier);
  5. }
  6. @Override
  7. protected T initialValue() {
  8. return supplier.get();
  9. }
  10. }