ThreadLocal初衷是在线程并发时,解决共享问题,但是由于过度设计,比如弱引用和哈希碰撞,导致理解难度增大,使用成本高,反而成为故障高发点,容易出现内存泄漏,脏数据,共享对象更新等问题。

关于引用类型

在垃圾回收GC里已经介绍过

  • 强引用 Strong Reference
  • 软引用 Soft Reference
  • 弱引用 Weak Reference
  • 虚引用 Phantom Reference

ThreadLocal 引入了WeakReference. 原意是ThreadLocal对象消失后,占用内存被GC掉,但是实际使用中,不当操作会出现内存泄漏风险。

应用

普通场景

在使用SpringBoot编写Web后端时,有时候需要手动创建一个应用上下文(Context)来在一个进程里传递数据

  1. public class UserContext {
  2. private static final ThreadLocal<String> user = new ThreadLocal<String>();
  3. public static void add(String userName) {
  4. user.set(userName);
  5. }
  6. public static void remove() {
  7. user.remove();
  8. }
  9. public static String getCurrentUserName() {
  10. return user.get();
  11. }
  12. }

然后在拦截器里注册

  1. @Component
  2. public class LoginInterceptor extends HandlerInterceptorAdapter {
  3. @Override
  4. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  5. HandlerMethod handlerMethod=(HandlerMethod)handler;
  6. Method method=handlerMethod.getMethod();
  7. // 。。。。
  8. if (claims != null) {
  9. // 将我们之前放到token中的userName给存到上下文对象中
  10. UserContext.add(claims.getSubject());
  11. return true;
  12. }
  13. return false;
  14. }
  15. @Override
  16. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  17. // 请求结束后要从上下文对象删除数据,如果不删除则可能会导致内存泄露
  18. UserContext.remove();
  19. super.afterCompletion(request, response, handler, ex);
  20. }
  21. }

这样可以在Service里使用

  1. String username = UserContext.getCurrentUserName();

Spring

Spring采用Threadlocal的方式,来保证单个线程中的数据库操作使用的是同一个数据库连接,同时,采用这种方式可以使业务层使用事务时不需要感知并管理connection对象,通过传播级别,巧妙地管理多个事务配置之间的切换,挂起和恢复。
Spring框架里面就是用的ThreadLocal来实现这种隔离,主要是在TransactionSynchronizationManager这个类里面,代码如下所示:

  1. private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);
  2. private static final ThreadLocal<Map<Object, Object>> resources =
  3. new NamedThreadLocal<>("Transactional resources");
  4. private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
  5. new NamedThreadLocal<>("Transaction synchronizations");
  6. private static final ThreadLocal<String> currentTransactionName =
  7. new NamedThreadLocal<>("Current transaction name");

Spring的事务主要是ThreadLocal和AOP去做实现的

源码

ThreadLocal可以实现线程之间数据的隔离,使用时只要先set,用的时候get,最后remove
线程使用ThreadLocal有三个重要的方法

  • set() 如果没有set操作的ThreadLocal,容易引起脏数据问题
  • get() 始终没有get操作的ThreadLocal对象是没有意义的
  • remove() 如果没有remove操作,容易引起内存泄漏。

    set()

    1. public void set(T value) {
    2. //获取当前线程
    3. Thread t = Thread.currentThread();
    4. //获取ThreadLocalMap对象
    5. ThreadLocalMap map = getMap(t);
    6. if (map != null)
    7. map.set(this, value);
    8. else
    9. createMap(t, value);
    10. }
    对于获取ThreadLocalMap对象的getMap()
    1. ThreadLocalMap getMap(Thread t) {
    2. return t.threadLocals;
    3. }
    4. //而Thraed中
    5. public class Thread implements Runnable {
    6. ThreadLocal.ThreadLocalMap threadLocals = null;
    7. ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    8. }
    即每个Thread维护了自己的ThreadLocals变量,实现了数据隔离

    对于ThreadLocalMap底层

  1. static class ThreadLocalMap {
  2. static class Entry extends WeakReference<ThreadLocal<?>> {
  3. /** The value associated with this ThreadLocal. */
  4. Object value;
  5. Entry(ThreadLocal<?> k, Object v) {
  6. super(k);
  7. value = v;
  8. }
  9. }
  10. /**
  11. * The initial capacity -- MUST be a power of two.
  12. */
  13. private static final int INITIAL_CAPACITY = 16;
  14. /**
  15. * The table, resized as necessary.
  16. * table.length MUST always be a power of two.
  17. */
  18. private Entry[] table;
  19. //。。。。
  20. }

ThreadLocalMap并未实现Map接口,而Entry是弱引用。底层使用了一个数组来存每个Entry
image.png

  • 一个Thread有且仅有1个ThreadLocalMap对象
  • 一个Entry对象的Key弱引用指向一个ThreadLocal对象
  • 1个ThreadLocal对象可以存储多个Entry对象
  • 一个ThreadLocal对象可以被多个对象共享
  • ThreadLocal对象不持有Value,Value由Entry对象持有

ThreadLocalMap中数组的存储

ThreadLocalMap在存储的时候会给每一个ThreadLocal对象一个threadLocalHashCode,在插入过程中,根据ThreadLocal对象的hash值,定位到table中的位置i,
int i = key.threadLocalHashCode & (len-1)

  1. private void set(ThreadLocal<?> key, Object value) {
  2. Entry[] tab = table;
  3. int len = tab.length;
  4. int i = key.threadLocalHashCode & (len-1);
  5. for (Entry e = tab[i];
  6. e != null;
  7. e = tab[i = nextIndex(i, len)]) {
  8. ThreadLocal<?> k = e.get();
  9. //如果位置i不为空,如果这个Entry对象的key正好是即将设置的key,那么就刷新Entry中的value;
  10. if (k == key) {
  11. e.value = value;
  12. return;
  13. }
  14. //如果当前位置是空的,就初始化一个Entry对象放在位置i上
  15. if (k == null) {
  16. replaceStaleEntry(key, value, i);
  17. return;
  18. }
  19. }
  20. tab[i] = new Entry(key, value);
  21. int sz = ++size;
  22. if (!cleanSomeSlots(i, sz) && sz >= threshold)
  23. rehash();
  24. }

如果位置i的不为空,而且key不等于entry,那就找下一个空位置,直到为空为止

image.png

get()

  1. private Entry getEntry(ThreadLocal<?> key) {
  2. int i = key.threadLocalHashCode & (table.length - 1);
  3. Entry e = table[i];
  4. if (e != null && e.get() == key)
  5. return e;
  6. else
  7. return getEntryAfterMiss(key, i, e);
  8. }
  9. private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
  10. Entry[] tab = table;
  11. int len = tab.length;
  12. // get的时候一样是根据ThreadLocal获取到table的i值,然后查找数据拿到后会对比key是否相等 if (e != null && e.get() == key)。
  13. while (e != null) {
  14. ThreadLocal<?> k = e.get();
  15. // 相等就直接返回,不相等就继续查找,找到相等位置。
  16. if (k == key)
  17. return e;
  18. if (k == null)
  19. expungeStaleEntry(i);
  20. else
  21. i = nextIndex(i, len);
  22. e = tab[i];
  23. }
  24. return null;
  25. }

内存存放

在Java中,栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存,而堆内存中的对象对所有线程可见,堆内存中的对象可以被所有线程访问。
ThreadLocal实例实际上也是被其创建的类持有(更顶端应该是被线程持有),而ThreadLocal的值其实也是被线程实例持有,它们都是位于堆上,只是通过一些技巧将可见性修改成了线程可见。

共享线程ThreadLocal数据

使用InheritableThreadLocal可以实现多个线程访问ThreadLocal的值,我们在主线程中创建一个InheritableThreadLocal的实例,然后在子线程中得到这个InheritableThreadLocal实例设置的值。
这样
ThreadLocal threadLocal = new InheritableThreadLocal();

  1. public class Thread implements Runnable {
  2. ……
  3. if (inheritThreadLocals && parent.inheritableThreadLocals != null)
  4. this.inheritableThreadLocals=ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
  5. ……
  6. }

如果线程的inheritThreadLocals变量不为空,比如我们上面的例子,而且父线程的inheritThreadLocals也存在,那么我就把父线程的inheritThreadLocals给当前线程的inheritThreadLocals。

createInheritedMap()其实是调用ThreadLocalMap的私有构造方法来产生一个实例对象,把父线程的不为null的变量的线程变量拷贝过来。

ThreadLocal副作用

对于ThreadLocal的弱引用,即使线程正在执行中,只要ThreadLocal对象引用被置为null,Entry的Key就会自动在下一次YGC时被回收,而在ThreadLocal使用Get() 和 Set() 时,又会自动地将那些key=null的value置为null,使value能够被垃圾回收,避免内存泄漏。但是事实并不如意。。。
正如ThreadLocal源码注释所述
ThreadLocal instances are typically private static fields in classess.
ThreadLocal对象通常作为私有静态变量使用,那么其生命周期至少不会随着线程的结束。
ThreadLocal会产生脏数据和内存泄漏,因为ThreadLocal有线程复用和内存常驻

脏数据

线程复用会产生脏数据。由于线程池会重用Thread对象,那么Thread绑定的类的静态属性ThreadLocal变量也会被重用。如果在实现的线程run() 方法体不显式地调用remove()清理与线程相关的ThreadLocal信息,那么如果下一线程不调用set()设置初始值,可能会get() 到重用信息

内存泄露

源码中使用static关键字修饰ThreadLocal ThreadLocal失去引用后,如果不进行remove(),那么这个线程执行后,通过ThraedLocal对象持有的String对象是不会被释放的。

贴一下ThreadLocal源代码(JDK8):

  1. /*
  2. * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
  3. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4. *
  5. * This code is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License version 2 only, as
  7. * published by the Free Software Foundation. Oracle designates this
  8. * particular file as subject to the "Classpath" exception as provided
  9. * by Oracle in the LICENSE file that accompanied this code.
  10. *
  11. * This code is distributed in the hope that it will be useful, but WITHOUT
  12. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  14. * version 2 for more details (a copy is included in the LICENSE file that
  15. * accompanied this code).
  16. *
  17. * You should have received a copy of the GNU General Public License version
  18. * 2 along with this work; if not, write to the Free Software Foundation,
  19. * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20. *
  21. * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22. * or visit www.oracle.com if you need additional information or have any
  23. * questions.
  24. */
  25. package java.lang;
  26. import java.lang.ref.*;
  27. import java.util.Objects;
  28. import java.util.concurrent.atomic.AtomicInteger;
  29. import java.util.function.Supplier;
  30. /**
  31. * This class provides thread-local variables. These variables differ from
  32. * their normal counterparts in that each thread that accesses one (via its
  33. * {@code get} or {@code set} method) has its own, independently initialized
  34. * copy of the variable. {@code ThreadLocal} instances are typically private
  35. * static fields in classes that wish to associate state with a thread (e.g.,
  36. * a user ID or Transaction ID).
  37. *
  38. * <p>For example, the class below generates unique identifiers local to each
  39. * thread.
  40. * A thread's id is assigned the first time it invokes {@code ThreadId.get()}
  41. * and remains unchanged on subsequent calls.
  42. * <pre>
  43. * import java.util.concurrent.atomic.AtomicInteger;
  44. *
  45. * public class ThreadId {
  46. * // Atomic integer containing the next thread ID to be assigned
  47. * private static final AtomicInteger nextId = new AtomicInteger(0);
  48. *
  49. * // Thread local variable containing each thread's ID
  50. * private static final ThreadLocal&lt;Integer&gt; threadId =
  51. * new ThreadLocal&lt;Integer&gt;() {
  52. * &#64;Override protected Integer initialValue() {
  53. * return nextId.getAndIncrement();
  54. * }
  55. * };
  56. *
  57. * // Returns the current thread's unique ID, assigning it if necessary
  58. * public static int get() {
  59. * return threadId.get();
  60. * }
  61. * }
  62. * </pre>
  63. * <p>Each thread holds an implicit reference to its copy of a thread-local
  64. * variable as long as the thread is alive and the {@code ThreadLocal}
  65. * instance is accessible; after a thread goes away, all of its copies of
  66. * thread-local instances are subject to garbage collection (unless other
  67. * references to these copies exist).
  68. *
  69. * @author Josh Bloch and Doug Lea
  70. * @since 1.2
  71. */
  72. public class ThreadLocal<T> {
  73. /**
  74. * ThreadLocals rely on per-thread linear-probe hash maps attached
  75. * to each thread (Thread.threadLocals and
  76. * inheritableThreadLocals). The ThreadLocal objects act as keys,
  77. * searched via threadLocalHashCode. This is a custom hash code
  78. * (useful only within ThreadLocalMaps) that eliminates collisions
  79. * in the common case where consecutively constructed ThreadLocals
  80. * are used by the same threads, while remaining well-behaved in
  81. * less common cases.
  82. */
  83. private final int threadLocalHashCode = nextHashCode();
  84. /**
  85. * The next hash code to be given out. Updated atomically. Starts at
  86. * zero.
  87. */
  88. private static AtomicInteger nextHashCode =
  89. new AtomicInteger();
  90. /**
  91. * The difference between successively generated hash codes - turns
  92. * implicit sequential thread-local IDs into near-optimally spread
  93. * multiplicative hash values for power-of-two-sized tables.
  94. */
  95. private static final int HASH_INCREMENT = 0x61c88647;
  96. /**
  97. * Returns the next hash code.
  98. */
  99. private static int nextHashCode() {
  100. return nextHashCode.getAndAdd(HASH_INCREMENT);
  101. }
  102. /**
  103. * Returns the current thread's "initial value" for this
  104. * thread-local variable. This method will be invoked the first
  105. * time a thread accesses the variable with the {@link #get}
  106. * method, unless the thread previously invoked the {@link #set}
  107. * method, in which case the {@code initialValue} method will not
  108. * be invoked for the thread. Normally, this method is invoked at
  109. * most once per thread, but it may be invoked again in case of
  110. * subsequent invocations of {@link #remove} followed by {@link #get}.
  111. *
  112. * <p>This implementation simply returns {@code null}; if the
  113. * programmer desires thread-local variables to have an initial
  114. * value other than {@code null}, {@code ThreadLocal} must be
  115. * subclassed, and this method overridden. Typically, an
  116. * anonymous inner class will be used.
  117. *
  118. * @return the initial value for this thread-local
  119. */
  120. protected T initialValue() {
  121. return null;
  122. }
  123. /**
  124. * Creates a thread local variable. The initial value of the variable is
  125. * determined by invoking the {@code get} method on the {@code Supplier}.
  126. *
  127. * @param <S> the type of the thread local's value
  128. * @param supplier the supplier to be used to determine the initial value
  129. * @return a new thread local variable
  130. * @throws NullPointerException if the specified supplier is null
  131. * @since 1.8
  132. */
  133. public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
  134. return new SuppliedThreadLocal<>(supplier);
  135. }
  136. /**
  137. * Creates a thread local variable.
  138. * @see #withInitial(java.util.function.Supplier)
  139. */
  140. public ThreadLocal() {
  141. }
  142. /**
  143. * Returns the value in the current thread's copy of this
  144. * thread-local variable. If the variable has no value for the
  145. * current thread, it is first initialized to the value returned
  146. * by an invocation of the {@link #initialValue} method.
  147. *
  148. * @return the current thread's value of this thread-local
  149. */
  150. public T get() {
  151. Thread t = Thread.currentThread();
  152. ThreadLocalMap map = getMap(t);
  153. if (map != null) {
  154. ThreadLocalMap.Entry e = map.getEntry(this);
  155. if (e != null) {
  156. @SuppressWarnings("unchecked")
  157. T result = (T)e.value;
  158. return result;
  159. }
  160. }
  161. return setInitialValue();
  162. }
  163. /**
  164. * Variant of set() to establish initialValue. Used instead
  165. * of set() in case user has overridden the set() method.
  166. *
  167. * @return the initial value
  168. */
  169. private T setInitialValue() {
  170. T value = initialValue();
  171. Thread t = Thread.currentThread();
  172. ThreadLocalMap map = getMap(t);
  173. if (map != null)
  174. map.set(this, value);
  175. else
  176. createMap(t, value);
  177. return value;
  178. }
  179. /**
  180. * Sets the current thread's copy of this thread-local variable
  181. * to the specified value. Most subclasses will have no need to
  182. * override this method, relying solely on the {@link #initialValue}
  183. * method to set the values of thread-locals.
  184. *
  185. * @param value the value to be stored in the current thread's copy of
  186. * this thread-local.
  187. */
  188. public void set(T value) {
  189. Thread t = Thread.currentThread();
  190. ThreadLocalMap map = getMap(t);
  191. if (map != null)
  192. map.set(this, value);
  193. else
  194. createMap(t, value);
  195. }
  196. /**
  197. * Removes the current thread's value for this thread-local
  198. * variable. If this thread-local variable is subsequently
  199. * {@linkplain #get read} by the current thread, its value will be
  200. * reinitialized by invoking its {@link #initialValue} method,
  201. * unless its value is {@linkplain #set set} by the current thread
  202. * in the interim. This may result in multiple invocations of the
  203. * {@code initialValue} method in the current thread.
  204. *
  205. * @since 1.5
  206. */
  207. public void remove() {
  208. ThreadLocalMap m = getMap(Thread.currentThread());
  209. if (m != null)
  210. m.remove(this);
  211. }
  212. /**
  213. * Get the map associated with a ThreadLocal. Overridden in
  214. * InheritableThreadLocal.
  215. *
  216. * @param t the current thread
  217. * @return the map
  218. */
  219. ThreadLocalMap getMap(Thread t) {
  220. return t.threadLocals;
  221. }
  222. /**
  223. * Create the map associated with a ThreadLocal. Overridden in
  224. * InheritableThreadLocal.
  225. *
  226. * @param t the current thread
  227. * @param firstValue value for the initial entry of the map
  228. */
  229. void createMap(Thread t, T firstValue) {
  230. t.threadLocals = new ThreadLocalMap(this, firstValue);
  231. }
  232. /**
  233. * Factory method to create map of inherited thread locals.
  234. * Designed to be called only from Thread constructor.
  235. *
  236. * @param parentMap the map associated with parent thread
  237. * @return a map containing the parent's inheritable bindings
  238. */
  239. static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
  240. return new ThreadLocalMap(parentMap);
  241. }
  242. /**
  243. * Method childValue is visibly defined in subclass
  244. * InheritableThreadLocal, but is internally defined here for the
  245. * sake of providing createInheritedMap factory method without
  246. * needing to subclass the map class in InheritableThreadLocal.
  247. * This technique is preferable to the alternative of embedding
  248. * instanceof tests in methods.
  249. */
  250. T childValue(T parentValue) {
  251. throw new UnsupportedOperationException();
  252. }
  253. /**
  254. * An extension of ThreadLocal that obtains its initial value from
  255. * the specified {@code Supplier}.
  256. */
  257. static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
  258. private final Supplier<? extends T> supplier;
  259. SuppliedThreadLocal(Supplier<? extends T> supplier) {
  260. this.supplier = Objects.requireNonNull(supplier);
  261. }
  262. @Override
  263. protected T initialValue() {
  264. return supplier.get();
  265. }
  266. }
  267. /**
  268. * ThreadLocalMap is a customized hash map suitable only for
  269. * maintaining thread local values. No operations are exported
  270. * outside of the ThreadLocal class. The class is package private to
  271. * allow declaration of fields in class Thread. To help deal with
  272. * very large and long-lived usages, the hash table entries use
  273. * WeakReferences for keys. However, since reference queues are not
  274. * used, stale entries are guaranteed to be removed only when
  275. * the table starts running out of space.
  276. */
  277. static class ThreadLocalMap {
  278. /**
  279. * The entries in this hash map extend WeakReference, using
  280. * its main ref field as the key (which is always a
  281. * ThreadLocal object). Note that null keys (i.e. entry.get()
  282. * == null) mean that the key is no longer referenced, so the
  283. * entry can be expunged from table. Such entries are referred to
  284. * as "stale entries" in the code that follows.
  285. */
  286. static class Entry extends WeakReference<ThreadLocal<?>> {
  287. /** The value associated with this ThreadLocal. */
  288. Object value;
  289. Entry(ThreadLocal<?> k, Object v) {
  290. super(k);
  291. value = v;
  292. }
  293. }
  294. /**
  295. * The initial capacity -- MUST be a power of two.
  296. */
  297. private static final int INITIAL_CAPACITY = 16;
  298. /**
  299. * The table, resized as necessary.
  300. * table.length MUST always be a power of two.
  301. */
  302. private Entry[] table;
  303. /**
  304. * The number of entries in the table.
  305. */
  306. private int size = 0;
  307. /**
  308. * The next size value at which to resize.
  309. */
  310. private int threshold; // Default to 0
  311. /**
  312. * Set the resize threshold to maintain at worst a 2/3 load factor.
  313. */
  314. private void setThreshold(int len) {
  315. threshold = len * 2 / 3;
  316. }
  317. /**
  318. * Increment i modulo len.
  319. */
  320. private static int nextIndex(int i, int len) {
  321. return ((i + 1 < len) ? i + 1 : 0);
  322. }
  323. /**
  324. * Decrement i modulo len.
  325. */
  326. private static int prevIndex(int i, int len) {
  327. return ((i - 1 >= 0) ? i - 1 : len - 1);
  328. }
  329. /**
  330. * Construct a new map initially containing (firstKey, firstValue).
  331. * ThreadLocalMaps are constructed lazily, so we only create
  332. * one when we have at least one entry to put in it.
  333. */
  334. ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
  335. table = new Entry[INITIAL_CAPACITY];
  336. int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
  337. table[i] = new Entry(firstKey, firstValue);
  338. size = 1;
  339. setThreshold(INITIAL_CAPACITY);
  340. }
  341. /**
  342. * Construct a new map including all Inheritable ThreadLocals
  343. * from given parent map. Called only by createInheritedMap.
  344. *
  345. * @param parentMap the map associated with parent thread.
  346. */
  347. private ThreadLocalMap(ThreadLocalMap parentMap) {
  348. Entry[] parentTable = parentMap.table;
  349. int len = parentTable.length;
  350. setThreshold(len);
  351. table = new Entry[len];
  352. for (int j = 0; j < len; j++) {
  353. Entry e = parentTable[j];
  354. if (e != null) {
  355. @SuppressWarnings("unchecked")
  356. ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
  357. if (key != null) {
  358. Object value = key.childValue(e.value);
  359. Entry c = new Entry(key, value);
  360. int h = key.threadLocalHashCode & (len - 1);
  361. while (table[h] != null)
  362. h = nextIndex(h, len);
  363. table[h] = c;
  364. size++;
  365. }
  366. }
  367. }
  368. }
  369. /**
  370. * Get the entry associated with key. This method
  371. * itself handles only the fast path: a direct hit of existing
  372. * key. It otherwise relays to getEntryAfterMiss. This is
  373. * designed to maximize performance for direct hits, in part
  374. * by making this method readily inlinable.
  375. *
  376. * @param key the thread local object
  377. * @return the entry associated with key, or null if no such
  378. */
  379. private Entry getEntry(ThreadLocal<?> key) {
  380. int i = key.threadLocalHashCode & (table.length - 1);
  381. Entry e = table[i];
  382. if (e != null && e.get() == key)
  383. return e;
  384. else
  385. return getEntryAfterMiss(key, i, e);
  386. }
  387. /**
  388. * Version of getEntry method for use when key is not found in
  389. * its direct hash slot.
  390. *
  391. * @param key the thread local object
  392. * @param i the table index for key's hash code
  393. * @param e the entry at table[i]
  394. * @return the entry associated with key, or null if no such
  395. */
  396. private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
  397. Entry[] tab = table;
  398. int len = tab.length;
  399. while (e != null) {
  400. ThreadLocal<?> k = e.get();
  401. if (k == key)
  402. return e;
  403. if (k == null)
  404. expungeStaleEntry(i);
  405. else
  406. i = nextIndex(i, len);
  407. e = tab[i];
  408. }
  409. return null;
  410. }
  411. /**
  412. * Set the value associated with key.
  413. *
  414. * @param key the thread local object
  415. * @param value the value to be set
  416. */
  417. private void set(ThreadLocal<?> key, Object value) {
  418. // We don't use a fast path as with get() because it is at
  419. // least as common to use set() to create new entries as
  420. // it is to replace existing ones, in which case, a fast
  421. // path would fail more often than not.
  422. Entry[] tab = table;
  423. int len = tab.length;
  424. int i = key.threadLocalHashCode & (len-1);
  425. for (Entry e = tab[i];
  426. e != null;
  427. e = tab[i = nextIndex(i, len)]) {
  428. ThreadLocal<?> k = e.get();
  429. if (k == key) {
  430. e.value = value;
  431. return;
  432. }
  433. if (k == null) {
  434. replaceStaleEntry(key, value, i);
  435. return;
  436. }
  437. }
  438. tab[i] = new Entry(key, value);
  439. int sz = ++size;
  440. if (!cleanSomeSlots(i, sz) && sz >= threshold)
  441. rehash();
  442. }
  443. /**
  444. * Remove the entry for key.
  445. */
  446. private void remove(ThreadLocal<?> key) {
  447. Entry[] tab = table;
  448. int len = tab.length;
  449. int i = key.threadLocalHashCode & (len-1);
  450. for (Entry e = tab[i];
  451. e != null;
  452. e = tab[i = nextIndex(i, len)]) {
  453. if (e.get() == key) {
  454. e.clear();
  455. expungeStaleEntry(i);
  456. return;
  457. }
  458. }
  459. }
  460. /**
  461. * Replace a stale entry encountered during a set operation
  462. * with an entry for the specified key. The value passed in
  463. * the value parameter is stored in the entry, whether or not
  464. * an entry already exists for the specified key.
  465. *
  466. * As a side effect, this method expunges all stale entries in the
  467. * "run" containing the stale entry. (A run is a sequence of entries
  468. * between two null slots.)
  469. *
  470. * @param key the key
  471. * @param value the value to be associated with key
  472. * @param staleSlot index of the first stale entry encountered while
  473. * searching for key.
  474. */
  475. private void replaceStaleEntry(ThreadLocal<?> key, Object value,
  476. int staleSlot) {
  477. Entry[] tab = table;
  478. int len = tab.length;
  479. Entry e;
  480. // Back up to check for prior stale entry in current run.
  481. // We clean out whole runs at a time to avoid continual
  482. // incremental rehashing due to garbage collector freeing
  483. // up refs in bunches (i.e., whenever the collector runs).
  484. int slotToExpunge = staleSlot;
  485. for (int i = prevIndex(staleSlot, len);
  486. (e = tab[i]) != null;
  487. i = prevIndex(i, len))
  488. if (e.get() == null)
  489. slotToExpunge = i;
  490. // Find either the key or trailing null slot of run, whichever
  491. // occurs first
  492. for (int i = nextIndex(staleSlot, len);
  493. (e = tab[i]) != null;
  494. i = nextIndex(i, len)) {
  495. ThreadLocal<?> k = e.get();
  496. // If we find key, then we need to swap it
  497. // with the stale entry to maintain hash table order.
  498. // The newly stale slot, or any other stale slot
  499. // encountered above it, can then be sent to expungeStaleEntry
  500. // to remove or rehash all of the other entries in run.
  501. if (k == key) {
  502. e.value = value;
  503. tab[i] = tab[staleSlot];
  504. tab[staleSlot] = e;
  505. // Start expunge at preceding stale entry if it exists
  506. if (slotToExpunge == staleSlot)
  507. slotToExpunge = i;
  508. cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
  509. return;
  510. }
  511. // If we didn't find stale entry on backward scan, the
  512. // first stale entry seen while scanning for key is the
  513. // first still present in the run.
  514. if (k == null && slotToExpunge == staleSlot)
  515. slotToExpunge = i;
  516. }
  517. // If key not found, put new entry in stale slot
  518. tab[staleSlot].value = null;
  519. tab[staleSlot] = new Entry(key, value);
  520. // If there are any other stale entries in run, expunge them
  521. if (slotToExpunge != staleSlot)
  522. cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
  523. }
  524. /**
  525. * Expunge a stale entry by rehashing any possibly colliding entries
  526. * lying between staleSlot and the next null slot. This also expunges
  527. * any other stale entries encountered before the trailing null. See
  528. * Knuth, Section 6.4
  529. *
  530. * @param staleSlot index of slot known to have null key
  531. * @return the index of the next null slot after staleSlot
  532. * (all between staleSlot and this slot will have been checked
  533. * for expunging).
  534. */
  535. private int expungeStaleEntry(int staleSlot) {
  536. Entry[] tab = table;
  537. int len = tab.length;
  538. // expunge entry at staleSlot
  539. tab[staleSlot].value = null;
  540. tab[staleSlot] = null;
  541. size--;
  542. // Rehash until we encounter null
  543. Entry e;
  544. int i;
  545. for (i = nextIndex(staleSlot, len);
  546. (e = tab[i]) != null;
  547. i = nextIndex(i, len)) {
  548. ThreadLocal<?> k = e.get();
  549. if (k == null) {
  550. e.value = null;
  551. tab[i] = null;
  552. size--;
  553. } else {
  554. int h = k.threadLocalHashCode & (len - 1);
  555. if (h != i) {
  556. tab[i] = null;
  557. // Unlike Knuth 6.4 Algorithm R, we must scan until
  558. // null because multiple entries could have been stale.
  559. while (tab[h] != null)
  560. h = nextIndex(h, len);
  561. tab[h] = e;
  562. }
  563. }
  564. }
  565. return i;
  566. }
  567. /**
  568. * Heuristically scan some cells looking for stale entries.
  569. * This is invoked when either a new element is added, or
  570. * another stale one has been expunged. It performs a
  571. * logarithmic number of scans, as a balance between no
  572. * scanning (fast but retains garbage) and a number of scans
  573. * proportional to number of elements, that would find all
  574. * garbage but would cause some insertions to take O(n) time.
  575. *
  576. * @param i a position known NOT to hold a stale entry. The
  577. * scan starts at the element after i.
  578. *
  579. * @param n scan control: {@code log2(n)} cells are scanned,
  580. * unless a stale entry is found, in which case
  581. * {@code log2(table.length)-1} additional cells are scanned.
  582. * When called from insertions, this parameter is the number
  583. * of elements, but when from replaceStaleEntry, it is the
  584. * table length. (Note: all this could be changed to be either
  585. * more or less aggressive by weighting n instead of just
  586. * using straight log n. But this version is simple, fast, and
  587. * seems to work well.)
  588. *
  589. * @return true if any stale entries have been removed.
  590. */
  591. private boolean cleanSomeSlots(int i, int n) {
  592. boolean removed = false;
  593. Entry[] tab = table;
  594. int len = tab.length;
  595. do {
  596. i = nextIndex(i, len);
  597. Entry e = tab[i];
  598. if (e != null && e.get() == null) {
  599. n = len;
  600. removed = true;
  601. i = expungeStaleEntry(i);
  602. }
  603. } while ( (n >>>= 1) != 0);
  604. return removed;
  605. }
  606. /**
  607. * Re-pack and/or re-size the table. First scan the entire
  608. * table removing stale entries. If this doesn't sufficiently
  609. * shrink the size of the table, double the table size.
  610. */
  611. private void rehash() {
  612. expungeStaleEntries();
  613. // Use lower threshold for doubling to avoid hysteresis
  614. if (size >= threshold - threshold / 4)
  615. resize();
  616. }
  617. /**
  618. * Double the capacity of the table.
  619. */
  620. private void resize() {
  621. Entry[] oldTab = table;
  622. int oldLen = oldTab.length;
  623. int newLen = oldLen * 2;
  624. Entry[] newTab = new Entry[newLen];
  625. int count = 0;
  626. for (int j = 0; j < oldLen; ++j) {
  627. Entry e = oldTab[j];
  628. if (e != null) {
  629. ThreadLocal<?> k = e.get();
  630. if (k == null) {
  631. e.value = null; // Help the GC
  632. } else {
  633. int h = k.threadLocalHashCode & (newLen - 1);
  634. while (newTab[h] != null)
  635. h = nextIndex(h, newLen);
  636. newTab[h] = e;
  637. count++;
  638. }
  639. }
  640. }
  641. setThreshold(newLen);
  642. size = count;
  643. table = newTab;
  644. }
  645. /**
  646. * Expunge all stale entries in the table.
  647. */
  648. private void expungeStaleEntries() {
  649. Entry[] tab = table;
  650. int len = tab.length;
  651. for (int j = 0; j < len; j++) {
  652. Entry e = tab[j];
  653. if (e != null && e.get() == null)
  654. expungeStaleEntry(j);
  655. }
  656. }
  657. }
  658. }