原理

内存泄漏

源码

  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. private final int threadLocalHashCode = nextHashCode();
  8. private static AtomicInteger nextHashCode =
  9. new AtomicInteger();
  10. private static final int HASH_INCREMENT = 0x61c88647;
  11. /**
  12. * Returns the next hash code.
  13. */
  14. private static int nextHashCode() {
  15. return nextHashCode.getAndAdd(HASH_INCREMENT);
  16. }
  17. protected T initialValue() {
  18. return null;
  19. }
  20. public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
  21. return new SuppliedThreadLocal<>(supplier);
  22. }
  23. /**
  24. * Creates a thread local variable.
  25. * @see #withInitial(java.util.function.Supplier)
  26. */
  27. public ThreadLocal() {
  28. }
  29. public T get() {
  30. Thread t = Thread.currentThread();
  31. ThreadLocalMap map = getMap(t);
  32. if (map != null) {
  33. ThreadLocalMap.Entry e = map.getEntry(this);
  34. if (e != null) {
  35. @SuppressWarnings("unchecked")
  36. T result = (T)e.value;
  37. return result;
  38. }
  39. }
  40. return setInitialValue();
  41. }
  42. private T setInitialValue() {
  43. T value = initialValue();
  44. Thread t = Thread.currentThread();
  45. ThreadLocalMap map = getMap(t);
  46. if (map != null)
  47. map.set(this, value);
  48. else
  49. createMap(t, value);
  50. return value;
  51. }
  52. public void set(T value) {
  53. Thread t = Thread.currentThread();
  54. ThreadLocalMap map = getMap(t);
  55. if (map != null)
  56. map.set(this, value);
  57. else
  58. createMap(t, value);
  59. }
  60. public void remove() {
  61. ThreadLocalMap m = getMap(Thread.currentThread());
  62. if (m != null)
  63. m.remove(this);
  64. }
  65. ThreadLocalMap getMap(Thread t) {
  66. return t.threadLocals;
  67. }
  68. void createMap(Thread t, T firstValue) {
  69. t.threadLocals = new ThreadLocalMap(this, firstValue);
  70. }
  71. static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
  72. return new ThreadLocalMap(parentMap);
  73. }
  74. T childValue(T parentValue) {
  75. throw new UnsupportedOperationException();
  76. }
  77. /**
  78. * An extension of ThreadLocal that obtains its initial value from
  79. * the specified {@code Supplier}.
  80. */
  81. static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
  82. private final Supplier<? extends T> supplier;
  83. SuppliedThreadLocal(Supplier<? extends T> supplier) {
  84. this.supplier = Objects.requireNonNull(supplier);
  85. }
  86. @Override
  87. protected T initialValue() {
  88. return supplier.get();
  89. }
  90. }
  91. static class ThreadLocalMap {
  92. static class Entry extends WeakReference<ThreadLocal<?>> {
  93. /** The value associated with this ThreadLocal. */
  94. Object value;
  95. Entry(ThreadLocal<?> k, Object v) {
  96. super(k);
  97. value = v;
  98. }
  99. }
  100. /**
  101. * The initial capacity -- MUST be a power of two.
  102. */
  103. private static final int INITIAL_CAPACITY = 16;
  104. /**
  105. * The table, resized as necessary.
  106. * table.length MUST always be a power of two.
  107. */
  108. private Entry[] table;
  109. /**
  110. * The number of entries in the table.
  111. */
  112. private int size = 0;
  113. /**
  114. * The next size value at which to resize.
  115. */
  116. private int threshold; // Default to 0
  117. /**
  118. * Set the resize threshold to maintain at worst a 2/3 load factor.
  119. */
  120. private void setThreshold(int len) {
  121. threshold = len * 2 / 3;
  122. }
  123. /**
  124. * Increment i modulo len.
  125. */
  126. private static int nextIndex(int i, int len) {
  127. return ((i + 1 < len) ? i + 1 : 0);
  128. }
  129. /**
  130. * Decrement i modulo len.
  131. */
  132. private static int prevIndex(int i, int len) {
  133. return ((i - 1 >= 0) ? i - 1 : len - 1);
  134. }
  135. /**
  136. * Construct a new map initially containing (firstKey, firstValue).
  137. * ThreadLocalMaps are constructed lazily, so we only create
  138. * one when we have at least one entry to put in it.
  139. */
  140. ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
  141. table = new Entry[INITIAL_CAPACITY];
  142. int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
  143. table[i] = new Entry(firstKey, firstValue);
  144. size = 1;
  145. setThreshold(INITIAL_CAPACITY);
  146. }
  147. /**
  148. * Construct a new map including all Inheritable ThreadLocals
  149. * from given parent map. Called only by createInheritedMap.
  150. *
  151. * @param parentMap the map associated with parent thread.
  152. */
  153. private ThreadLocalMap(ThreadLocalMap parentMap) {
  154. Entry[] parentTable = parentMap.table;
  155. int len = parentTable.length;
  156. setThreshold(len);
  157. table = new Entry[len];
  158. for (int j = 0; j < len; j++) {
  159. Entry e = parentTable[j];
  160. if (e != null) {
  161. @SuppressWarnings("unchecked")
  162. ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
  163. if (key != null) {
  164. Object value = key.childValue(e.value);
  165. Entry c = new Entry(key, value);
  166. int h = key.threadLocalHashCode & (len - 1);
  167. while (table[h] != null)
  168. h = nextIndex(h, len);
  169. table[h] = c;
  170. size++;
  171. }
  172. }
  173. }
  174. }
  175. private Entry getEntry(ThreadLocal<?> key) {
  176. int i = key.threadLocalHashCode & (table.length - 1);
  177. Entry e = table[i];
  178. if (e != null && e.get() == key)
  179. return e;
  180. else
  181. return getEntryAfterMiss(key, i, e);
  182. }
  183. /**
  184. * Version of getEntry method for use when key is not found in
  185. * its direct hash slot.
  186. *
  187. * @param key the thread local object
  188. * @param i the table index for key's hash code
  189. * @param e the entry at table[i]
  190. * @return the entry associated with key, or null if no such
  191. */
  192. private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
  193. Entry[] tab = table;
  194. int len = tab.length;
  195. while (e != null) {
  196. ThreadLocal<?> k = e.get();
  197. if (k == key)
  198. return e;
  199. if (k == null)
  200. expungeStaleEntry(i);
  201. else
  202. i = nextIndex(i, len);
  203. e = tab[i];
  204. }
  205. return null;
  206. }
  207. /**
  208. * Set the value associated with key.
  209. *
  210. * @param key the thread local object
  211. * @param value the value to be set
  212. */
  213. private void set(ThreadLocal<?> key, Object value) {
  214. // We don't use a fast path as with get() because it is at
  215. // least as common to use set() to create new entries as
  216. // it is to replace existing ones, in which case, a fast
  217. // path would fail more often than not.
  218. Entry[] tab = table;
  219. int len = tab.length;
  220. int i = key.threadLocalHashCode & (len-1);
  221. for (Entry e = tab[i];
  222. e != null;
  223. e = tab[i = nextIndex(i, len)]) {
  224. ThreadLocal<?> k = e.get();
  225. if (k == key) {
  226. e.value = value;
  227. return;
  228. }
  229. if (k == null) {
  230. replaceStaleEntry(key, value, i);
  231. return;
  232. }
  233. }
  234. tab[i] = new Entry(key, value);
  235. int sz = ++size;
  236. if (!cleanSomeSlots(i, sz) && sz >= threshold)
  237. rehash();
  238. }
  239. /**
  240. * Remove the entry for key.
  241. */
  242. private void remove(ThreadLocal<?> key) {
  243. Entry[] tab = table;
  244. int len = tab.length;
  245. int i = key.threadLocalHashCode & (len-1);
  246. for (Entry e = tab[i];
  247. e != null;
  248. e = tab[i = nextIndex(i, len)]) {
  249. if (e.get() == key) {
  250. e.clear();
  251. expungeStaleEntry(i);
  252. return;
  253. }
  254. }
  255. }
  256. private void replaceStaleEntry(ThreadLocal<?> key, Object value,
  257. int staleSlot) {
  258. Entry[] tab = table;
  259. int len = tab.length;
  260. Entry e;
  261. // Back up to check for prior stale entry in current run.
  262. // We clean out whole runs at a time to avoid continual
  263. // incremental rehashing due to garbage collector freeing
  264. // up refs in bunches (i.e., whenever the collector runs).
  265. int slotToExpunge = staleSlot;
  266. for (int i = prevIndex(staleSlot, len);
  267. (e = tab[i]) != null;
  268. i = prevIndex(i, len))
  269. if (e.get() == null)
  270. slotToExpunge = i;
  271. // Find either the key or trailing null slot of run, whichever
  272. // occurs first
  273. for (int i = nextIndex(staleSlot, len);
  274. (e = tab[i]) != null;
  275. i = nextIndex(i, len)) {
  276. ThreadLocal<?> k = e.get();
  277. // If we find key, then we need to swap it
  278. // with the stale entry to maintain hash table order.
  279. // The newly stale slot, or any other stale slot
  280. // encountered above it, can then be sent to expungeStaleEntry
  281. // to remove or rehash all of the other entries in run.
  282. if (k == key) {
  283. e.value = value;
  284. tab[i] = tab[staleSlot];
  285. tab[staleSlot] = e;
  286. // Start expunge at preceding stale entry if it exists
  287. if (slotToExpunge == staleSlot)
  288. slotToExpunge = i;
  289. cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
  290. return;
  291. }
  292. // If we didn't find stale entry on backward scan, the
  293. // first stale entry seen while scanning for key is the
  294. // first still present in the run.
  295. if (k == null && slotToExpunge == staleSlot)
  296. slotToExpunge = i;
  297. }
  298. // If key not found, put new entry in stale slot
  299. tab[staleSlot].value = null;
  300. tab[staleSlot] = new Entry(key, value);
  301. // If there are any other stale entries in run, expunge them
  302. if (slotToExpunge != staleSlot)
  303. cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
  304. }
  305. /**
  306. * Expunge a stale entry by rehashing any possibly colliding entries
  307. * lying between staleSlot and the next null slot. This also expunges
  308. * any other stale entries encountered before the trailing null. See
  309. * Knuth, Section 6.4
  310. *
  311. * @param staleSlot index of slot known to have null key
  312. * @return the index of the next null slot after staleSlot
  313. * (all between staleSlot and this slot will have been checked
  314. * for expunging).
  315. */
  316. private int expungeStaleEntry(int staleSlot) {
  317. Entry[] tab = table;
  318. int len = tab.length;
  319. // expunge entry at staleSlot
  320. tab[staleSlot].value = null;
  321. tab[staleSlot] = null;
  322. size--;
  323. // Rehash until we encounter null
  324. Entry e;
  325. int i;
  326. for (i = nextIndex(staleSlot, len);
  327. (e = tab[i]) != null;
  328. i = nextIndex(i, len)) {
  329. ThreadLocal<?> k = e.get();
  330. if (k == null) {
  331. e.value = null;
  332. tab[i] = null;
  333. size--;
  334. } else {
  335. int h = k.threadLocalHashCode & (len - 1);
  336. if (h != i) {
  337. tab[i] = null;
  338. // Unlike Knuth 6.4 Algorithm R, we must scan until
  339. // null because multiple entries could have been stale.
  340. while (tab[h] != null)
  341. h = nextIndex(h, len);
  342. tab[h] = e;
  343. }
  344. }
  345. }
  346. return i;
  347. }
  348. /**
  349. * Heuristically scan some cells looking for stale entries.
  350. * This is invoked when either a new element is added, or
  351. * another stale one has been expunged. It performs a
  352. * logarithmic number of scans, as a balance between no
  353. * scanning (fast but retains garbage) and a number of scans
  354. * proportional to number of elements, that would find all
  355. * garbage but would cause some insertions to take O(n) time.
  356. *
  357. * @param i a position known NOT to hold a stale entry. The
  358. * scan starts at the element after i.
  359. *
  360. * @param n scan control: {@code log2(n)} cells are scanned,
  361. * unless a stale entry is found, in which case
  362. * {@code log2(table.length)-1} additional cells are scanned.
  363. * When called from insertions, this parameter is the number
  364. * of elements, but when from replaceStaleEntry, it is the
  365. * table length. (Note: all this could be changed to be either
  366. * more or less aggressive by weighting n instead of just
  367. * using straight log n. But this version is simple, fast, and
  368. * seems to work well.)
  369. *
  370. * @return true if any stale entries have been removed.
  371. */
  372. private boolean cleanSomeSlots(int i, int n) {
  373. boolean removed = false;
  374. Entry[] tab = table;
  375. int len = tab.length;
  376. do {
  377. i = nextIndex(i, len);
  378. Entry e = tab[i];
  379. if (e != null && e.get() == null) {
  380. n = len;
  381. removed = true;
  382. i = expungeStaleEntry(i);
  383. }
  384. } while ( (n >>>= 1) != 0);
  385. return removed;
  386. }
  387. /**
  388. * Re-pack and/or re-size the table. First scan the entire
  389. * table removing stale entries. If this doesn't sufficiently
  390. * shrink the size of the table, double the table size.
  391. */
  392. private void rehash() {
  393. expungeStaleEntries();
  394. // Use lower threshold for doubling to avoid hysteresis
  395. if (size >= threshold - threshold / 4)
  396. resize();
  397. }
  398. /**
  399. * Double the capacity of the table.
  400. */
  401. private void resize() {
  402. Entry[] oldTab = table;
  403. int oldLen = oldTab.length;
  404. int newLen = oldLen * 2;
  405. Entry[] newTab = new Entry[newLen];
  406. int count = 0;
  407. for (int j = 0; j < oldLen; ++j) {
  408. Entry e = oldTab[j];
  409. if (e != null) {
  410. ThreadLocal<?> k = e.get();
  411. if (k == null) {
  412. e.value = null; // Help the GC
  413. } else {
  414. int h = k.threadLocalHashCode & (newLen - 1);
  415. while (newTab[h] != null)
  416. h = nextIndex(h, newLen);
  417. newTab[h] = e;
  418. count++;
  419. }
  420. }
  421. }
  422. setThreshold(newLen);
  423. size = count;
  424. table = newTab;
  425. }
  426. /**
  427. * Expunge all stale entries in the table.
  428. */
  429. private void expungeStaleEntries() {
  430. Entry[] tab = table;
  431. int len = tab.length;
  432. for (int j = 0; j < len; j++) {
  433. Entry e = tab[j];
  434. if (e != null && e.get() == null)
  435. expungeStaleEntry(j);
  436. }
  437. }
  438. }
  439. }