ThreadLocal源码分析

package java.lang;
import java.lang.ref.*;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;

/**
 * 该类提供线程局部变量.  
 * 这种变量在多线程环境下访问(通过get或set方法访问)时能保证各个线程里的变量相对独立于其他线程内的变量。
 * 该实例通常是在类中定义为 private static fields,用于关联线程和线程的上下文。(例如:a user ID or Transaction ID).
 * 例如: 下面的类生成每个线程本地的唯一标识符。
 * 线程的id在第一次调用时被分配 {@code ThreadId.get()},并在随后的调用中保持不变 .
 * 
 * import java.util.concurrent.atomic.AtomicInteger;
 *
 * public class ThreadId {
 *     // 包含要分配的下一个线程id的原子整数 
 *     private static final AtomicInteger nextId = new AtomicInteger(0);
 *
 *     // 包含每个线程id的线程局部变量 
 *     private static final ThreadLocal threadId =
 *         new ThreadLocal() {
               @Override 
               protected Integer initialValue() {
 *                 return nextId.getAndIncrement();
 *         }
 *     };
 *
 *     // 返回当前线程的唯一ID
 *     public static int get() {
 *         return threadId.get();
 *     }
 * }
 * 
* 只要线程{@code ThreadLocal}是活动的且实例是可访问的,则每个线程都保持对线程本地变量的副本的隐式引用。 * 一个线程结束后,所有线程本地实例的副本都受垃圾回收的影响。(除非存在对这些副本的其他引用) */ public class ThreadLocal { /** * ThreadLocals rely on per-thread linear-probe hash maps attached to each thread (Thread.threadLocals and * inheritableThreadLocals). * ThreadLocal 对象充当键,通过threadLocalHashCode搜索。(将会用于在ThreadLocalMap中找到ThreadLocal对应的value值) * 这是一种自定义的 hash code(仅仅在ThreadLocalMaps中使用), * 为了排除在相同线程下连续使用构造器实例化ThreadLocals出现的hashcode碰撞冲突,在较少情况下也能保持良好的表现 */ private final int threadLocalHashCode = nextHashCode(); /** * 下个hasCode,从0开始,原子级更新 */ private static AtomicInteger nextHashCode = new AtomicInteger(); /** * 在一个 AtomicInteger 变量(初始值为0)的基础上每次累加 0x61c88647,使用 AtomicInteger 为了保证每次的加法是原子操作。 * 而 0x61c88647 这个就比较神奇了,它可以使 hashcode 均匀的分布在大小为 2 的 N 次方的数组里。 */ private static final int HASH_INCREMENT = 0x61c88647; /** * 返回下一个hash code. */ private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); } /** * * 当线程第一次访问变量时 {@link #get},将调用此方法, * 除非线程先前调用了该方法{@link #set},在这种情况下,将不会为线程调用该方法。 * * 通常,每个线程最多调用一次此方法,但在后续调用了{@link #remove} 的情况下,可以再次调用该方法{@link #get}。 * *

* 此实现只是返回{@code null}; * 如果程序员希望线程局部变量有一个初始值而不是{@code null},{@code ThreadLocal}必须被子类化 ,然后重写此方法. * 通常,会使用匿名内部类。 */ protected T initialValue() { return null; } /** * 创建 ThreadLocal 局部变量。 * 变量的初始值是通过调用{@code get}上的{@code Supplier}方法来确定的。 * * @param thread local 类型 * @param supplier 用于确定初始值的 supplier * @return 一个新的 thread local * @throws NullPointerException 如果 supplier 是null * @since 1.8 */ public static ThreadLocal withInitial(Supplier supplier) { return new SuppliedThreadLocal<>(supplier); } /** * 默认构造器 */ public ThreadLocal() { } /** * 返回当前ThreadLocal变量中的 thread 副本。 * 如果对于当前 thread,ThreadLocalMap没有值,那么将调用{@link #initialValue} 方法初始化值 * * @return thread-local中当前thread的值 */ public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); } /** * 创建 初始值的方法 set()。 * 如果 开发者 重写set(),将使用此set() * * @return 初始值 */ 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; } /** * 大多数子类并不需要重写此方法,只依赖于{@link #initialValue}方法设置 thread-locals 的值 * * @param value 值将会被存储在当前ThreadLocal中 */ public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } /** * 清除 ThreadLocalMap 中当前thread的值。 * 如果此 thread-local 被当前 thread 读取{@linkplain #get read},且这期间当前线程没有设置其值,则调用其{@link #initialValue}方法重新初始化其值。 * 这将导致在当前线程中多次调用 {@link #initialValue}方法 * If this thread-local variable is subsequently * * @since 1.5 */ public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); } /** * 通过ThreadLocal获取关联的map。 * 在InheritableThreadLocal中重写。 * * @param t 当前线程 * @return ThreadLocalMap */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; } /** * 根据currentThread 和 fistValue 创建 当前线程的 ThreadLocalMap * * @param t 当前线程 * @param firstValue map中的第一个entry */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } /** * 通过 parentMap 创建对应的 ThreadLocalMap * 设计为 只被 Thread 构造器。 * * @param parentMap 与parent thread关联的ThreadLocalMap * @return 与包含parentMap的map */ static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { return new ThreadLocalMap(parentMap); } /** * childValue() 显然是定义在 可继承ThradLocal 的子类中, * 但这里是内部定义的,目的是提供 createInheritedMap 工厂方法,而不需要在InheritableThreadLocal的子类中映射类。 * * 这种技巧比在方法中嵌入测试实例更好。 */ T childValue(T parentValue) { throw new UnsupportedOperationException(); } /** * ThreadLocal的扩展,从具体的 {@code Supplier} 获得初始值 */ static final class SuppliedThreadLocal extends ThreadLocal { private final Supplier supplier; SuppliedThreadLocal(Supplier supplier) { this.supplier = Objects.requireNonNull(supplier); } @Override protected T initialValue() { return supplier.get(); } } /** ThreadLocalMap是一个定制的 hash map,只适合于维护 thread local。 在ThreadLocal类之外不导出任何操作。 这个类是 package private,允许声明类 Thread 中的 fields。 为了帮助处理非常大的和long-lived usages,哈希表条目使用WeakReferences作为键。 To help deal with * very large and long-lived usages, the hash table entries use * WeakReferences for keys. However, since reference queues are not * used, stale entries are guaranteed to be removed only when * the table starts running out of space. */ static class ThreadLocalMap { /** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). 在当前 ThreadLocalMap 中 如果 key Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ static class Entry extends WeakReference> { /** 与ThreadLocal关联的值 */ Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } } /** * 初始容量-- MUST be a power of two. */ private static final int INITIAL_CAPACITY = 16; /** * table,如果需要调整大小,通常会是原来的2倍 */ private Entry[] table; /** * table中 entryies 的数量 */ private int size = 0; /** * 要调整的下一个 size value。 */ private int threshold; // 默认为 0 /** * 最坏情况下 设置容量的调整阈值为 2/3 负载因子,不再以倍数增长 */ private void setThreshold(int len) { threshold = len * 2 / 3; } /** * 轮询下一个 */ private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); } /** * 轮询上一个 */ private static int prevIndex(int i, int len) { return ((i - 1 >= 0) ? i - 1 : len - 1); } /** * 构造器,包含(firstKey, firstValue)。 * ThreadLocalMaps 是延迟初始化的,只有在 最少一个 entry 放入其才会初始化 */ 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); } /** * 从已给的 parentMap 构造新的 map 包含所有可继承的 ThreadLocals * 私有,只被 createInheritedMap() 调用。 * * @param parentMap 与 parent thread 关联的map. */ 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 key = (ThreadLocal) 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++; } } } } /** * 获取与key关联的entry。 * 此方法为了快速命中 目录中的 entry * 如果为null,则使用 getEntryAfterMiss()方法,这样设计为了最大限度提高 命中率,更加快速、便捷 * * @param key thread local变量 * @return 与 传值key关联的 entry,如果没有则返回null */ 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); } /** * 当 key 没有在此目录中找到,将会使用此方法 * * @param key threadLocal 对象 * @param i key的hash code 的表索引 * @param e entry[] 位置i的元素 * @return 与key关联的entry,如果没有则为Null */ 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; } /** * 设置与 key 关联的值 * @param key ThreadLocal 对象 * @param value ThreadLocal 存储的值 */ private void set(ThreadLocal key, Object value) { // We don't use a fast path as with get() because it is at // least as common to use set() to create new entries as // it is to replace existing ones, in which case, a fast // path would fail more often than not. 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(); //如果找到了,直接设置 value 值即可 if (k == key) { e.value = value; return; } //可以认定 k 已经没有引用了。 if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } /** * 根据key删除 table 中的元素 */ 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; } } } /** * 替换一个过期的 entry,在对特定key操作期间。 * 无论指定的key中是否已存在 entry,值将被存储到entry中。 * As a side effect, this method expunges all stale entries in the * "run" containing the stale entry. (A run is a sequence of entries * between two null slots.) * * @param key the key * @param value 关联key的值 * @param staleSlot index of the first stale entry encountered while * searching for key. */ private void replaceStaleEntry(ThreadLocal key, Object value, int staleSlot) { Entry[] tab = table; int len = tab.length; Entry e; // Back up to check for prior stale entry in current run. // We clean out whole runs at a time to avoid continual // incremental rehashing due to garbage collector freeing // up refs in bunches (i.e., whenever the collector runs). int slotToExpunge = staleSlot; for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len)) if (e.get() == null) slotToExpunge = i; // Find either the key or trailing null slot of run, whichever // occurs first for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal k = e.get(); // 如果找到key,那么需要用过期的 entry 交换它,以保证 hash table 的顺序 // // The newly stale slot, or any other stale slot // encountered above it, can then be sent to expungeStaleEntry // to remove or rehash all of the other entries in run. if (k == key) { e.value = value; tab[i] = tab[staleSlot]; tab[staleSlot] = e; // 如果存在,开始清除之前过期的 entry if (slotToExpunge == staleSlot) slotToExpunge = i; cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); return; } // If we didn't find stale entry on backward scan, the // first stale entry seen while scanning for key is the // first still present in the run. if (k == null && slotToExpunge == staleSlot) slotToExpunge = i; } // 如果key不存在,则 向过期的 slot中放入新的entry tab[staleSlot].value = null; tab[staleSlot] = new Entry(key, value); // 如果运行时有其他过期的entries,则删除 if (slotToExpunge != staleSlot) cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); } /** 删除过期的 entry, * Expunge a stale entry by rehashing any possibly colliding entries * lying between staleSlot and the next null slot. This also expunges any other stale entries encountered before the trailing null. 这也会删除 见 Knuth,Section 6.4 * * @param staleSlot index of slot known to have null key * @return the index of the next null slot after staleSlot * (all between staleSlot and this slot will have been checked * for expunging). */ private int expungeStaleEntry(int staleSlot) { Entry[] tab = table; int len = tab.length; // 删除在 table 中 staleSlot 位置的元素 tab[staleSlot].value = null; tab[staleSlot] = null; size--;// 计数减少1 // rehash 直到遇到下一个null的值 Entry e; int i; for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal k = e.get(); // 如果为 null 说明当前 e 对应的 ThreadLocal 已经没有引用 if (k == null) { e.value = null; tab[i] = null; size--; } else { int h = k.threadLocalHashCode & (len - 1); if (h != i) { tab[i] = null; //与Knuth6.4算法R不同,我们必须扫描到NULL,因为多个entries可能已经过时。 while (tab[h] != null) h = nextIndex(h, len); tab[h] = e; } } } return i; } /** * Heuristically scan some cells looking for stale entries. 扫描一些单元格寻找过期的entries。 这将在添加新元素或删除一个过期的元素时调用。 It performs a logarithmic number of scans, as a balance between no scanning (fast but retains garbage) and a number of scans proportional to number of elements, that would find all garbage but would cause some insertions to take O(n) time. * * @param i 非过期entry的位置。从i开始扫描元素。 * * @param n scan control: {@code log2(n)} cells are scanned, * unless a stale entry is found, in which case * {@code log2(table.length)-1} additional cells are scanned. * When called from insertions, this parameter is the number * of elements, but when from replaceStaleEntry, it is the * table length. (Note: all this could be changed to be either * more or less aggressive by weighting n instead of just * using straight log n. 但是此版本简单、快速并运行良好。) * * @return 如果已删除过期的entry,则返回true */ private boolean cleanSomeSlots(int i, int n) { boolean removed = false; Entry[] tab = table; int len = tab.length; do { // 下个位置 i = nextIndex(i, len); Entry e = tab[i]; if (e != null && e.get() == null) { // 数组的长度 n = len; removed = true; // 清除在i位置过期entry项 i = expungeStaleEntry(i); } } while ( (n >>>= 1) != 0); return removed; } /** * 调整table的容量: * 1:首先扫描并清除过期entries; * 2:清除后容量还不够,则进行2倍方式扩容。 */ private void rehash() { // 清除所有在table中过期的entry expungeStaleEntries(); // Use lower threshold for doubling to avoid hysteresis if (size >= threshold - threshold / 4) resize(); } /** * table 的容量翻倍 */ private void resize() { Entry[] oldTab = table; int oldLen = oldTab.length; // 容量扩增为原来的2倍 int newLen = oldLen * 2; Entry[] newTab = new Entry[newLen]; int count = 0; for (int j = 0; j < oldLen; ++j) { Entry e = oldTab[j]; // 如果 此元素 不为空 if (e != null) { ThreadLocal k = e.get(); if (k == null) { // thread-local 清除此元素 e.value = null; // Help the GC } else { int h = k.threadLocalHashCode & (newLen - 1); while (newTab[h] != null) h = nextIndex(h, newLen); newTab[h] = e; count++; } } } setThreshold(newLen); size = count; table = newTab; } /** * 清除所有在table中过期的entry */ private void expungeStaleEntries() { Entry[] tab = table; int len = tab.length; for (int j = 0; j < len; j++) { Entry e = tab[j]; if (e != null && e.get() == null) expungeStaleEntry(j); } } } }

你可能感兴趣的:(ThreadLocal源码分析)