10 java.lang.ThreadLocal
来源:互联网 发布:安卓中文编程软件 编辑:程序博客网 时间:2024/06/06 12:46
ThreadLocal
2015.03.23 By 970655147
说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个
ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度
下面是几幅流程图
插入数据
移除数据
start ->
声明
, 大家可以看看注释
/** * This class provides thread-local variables. These variables differ from * their normal counterparts in that each thread that accesses one (via its * <tt>get</tt> or <tt>set</tt> method) has its own, independently initialized * copy of the variable. <tt>ThreadLocal</tt> instances are typically private * static fields in classes that wish to associate state with a thread (e.g., * a user ID or Transaction ID). * * <p>For example, the class below generates unique identifiers local to each * thread. * A thread's id is assigned the first time it invokes <tt>ThreadId.get()</tt> * and remains unchanged on subsequent calls. * <pre> * import java.util.concurrent.atomic.AtomicInteger; * * public class ThreadId { * // Atomic integer containing the next thread ID to be assigned * private static final AtomicInteger nextId = new AtomicInteger(0); * * // Thread local variable containing each thread's ID * private static final ThreadLocal<Integer> threadId = * new ThreadLocal<Integer>() { * @Override protected Integer initialValue() { * return nextId.getAndIncrement(); * } * }; * * // Returns the current thread's unique ID, assigning it if necessary * public static int get() { * return threadId.get(); * } * } * </pre> * <p>Each thread holds an implicit reference to its copy of a thread-local * variable as long as the thread is alive and the <tt>ThreadLocal</tt> * instance is accessible; after a thread goes away, all of its copies of * thread-local instances are subject to garbage collection (unless other * references to these copies exist). * * @author Josh Bloch and Doug Lea * @since 1.2 */public class ThreadLocal<T>
注 :
Entry中key为空而Value不为空的情况 : ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用引用他,那么系统gc的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry
ThreadLocal. 属性
// 当前threadLocal对象的hashCode// threadLocal对象的hashCode生成器 [cas操作保证线程安全]// 相邻创建的两个threadLocal的hashCode的差值private final int threadLocalHashCode = nextHashCode();/** * The next hash code to be given out. Updated atomically. Starts at * zero. */private static AtomicInteger nextHashCode = new AtomicInteger();/** * The difference between successively generated hash codes - turns * implicit sequential thread-local IDs into near-optimally spread * multiplicative hash values for power-of-two-sized tables. */private static final int HASH_INCREMENT = 0x61c88647;
ThreadLocal. ThreadLocal()
public ThreadLocal() {}
ThreadLocal. get()
public T get() { // 获取当前线程对应的ThreadLocalMap // 如果map不为null 获取该map中this对应的entry 进而获取当前线程对应当前threadLocal对应的数据// 如果该数据不为null 返回 // 否则 设置当前线程对应当前threadLocal对应的数据为initialValue() // 否则 为当前线程创建threadLocalMap 并且设置当前线程对应当前threadLocal对应的数据为initialValue() Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); }ThreadLocal. getMap(Thread t) ThreadLocalMap getMap(Thread t) { return t.threadLocals; }ThreadLocal$ThreadLocalMap. getEntry(ThreadLocal key) private Entry getEntry(ThreadLocal key) { // 通过key的threadLocalHashCode计算出当前线程存储的对象的索引 如果该索引对应的entry不为null 并且该entry.get == key 返回该entry // 在该索引之后到下一个空entry之间查找key匹配的entry 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); }ThreadLocal$ThreadLocalMap. getEntryAfterMiss(ThreadLocal key, int i, Entry e) private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) { Entry[] tab = table; int len = tab.length; // 从第i开始遍历 直到tab[index]为null[下一个为null的entry] // 如果e.threadLocal 和key是同一个对象 返回e // 如果e.threadLocal为null[key被gc] 清空该元素, 以及更新各个符合条件的entry的位置 // 否则更新index // 如果没有找到 则表示没有该key对应的entry /* 因为存放的时候是先查找[index = threadLocal.hashCode & len] 如果该index的entry为null 或者该entry对应的key即为当前threadLocal 则设置值 否则查找到下一个key被gc 或者为null的entry设置值 而且每一次修改Map的时候, 会做一些维护性质的操作 */ 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; }ThreadLocal$ThreadLocalMap. nextIndex(int i, int len) private static int nextIndex(int i, int len) { // 如果i+1没有超过len 则返回i+1 否则返回0, 构成一个循环 return ((i + 1 < len) ? i + 1 : 0); }ThreadLocal$ThreadLocalMap. expungeStaleEntry(int staleSlot) private int expungeStaleEntry(int staleSlot) { // 对于第staleSlot之后的元素, 直到碰到null元素, 清空key被gc的元素, 向前移动符合条件的非空元素 Entry[] tab = table; int len = tab.length; // expunge entry at staleSlot // 清除第i个entry的数据 更新size tab[staleSlot].value = null; tab[staleSlot] = null; size--; // Rehash until we encounter null Entry e; int i; // 从slateSlot向后循环遍历 直到tab[nextIndex]为null 不在循环 for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal k = e.get(); // 如果该entry依赖的threadLocal为null[key被gc的entry] 清除该entry的数据 更新size // 否则 再次获取该threadLocal对应的索引 如果该索引不为i 清除第i个entry的数据 然后将数据复制到h之后第一个entry为null的entry处 if (k == null) { e.value = null; tab[i] = null; size--; } else { // 在次获取该threadLocal的存放值的索引 如果h不为当前的索引则 清理tab[i] 将数据复制到h之后第一个entry为null的entry处 int h = k.threadLocalHashCode & (len - 1); if (h != i) { tab[i] = null; // Unlike Knuth 6.4 Algorithm R, we must scan until // null because multiple entries could have been stale. // 找到h之后第一个entry为null的索引 给该entry赋值 while (tab[h] != null) h = nextIndex(h, len); tab[h] = e; } } } return i; }ThreadLocal. setInitialValue() private T setInitialValue () { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); // 获取当前线程的threadLocalMap 设置默认值 如果该map为null 则创建一个threadLocalMap 并设置初始值 // 返回initialValue if (map != null) map.set(this, value); else createMap(t, value); return value; }ThreadLocal. createMap(Thread t, T firstValue)void createMap(Thread t, T firstValue) { // 创建一个ThreadLocalMap 并添加一个初值 t.threadLocals = new ThreadLocalMap(this, firstValue);}
附上一张 ThreadLocal$ThreadLocalMap. expungeStaleEntry(int staleSlot) 的流程图
ThreadLocal. set(T value)
public void set(T value) { // 获取当前线程的threadLocalMap 如果map为null 创建一个 // 添加value到该threadLocalMap Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }ThreadLocal. createMap(Thread t, T firstValue)void createMap(Thread t, T firstValue) { // 创建一个ThreadLocalMap 并添加一个初值 t.threadLocals = new ThreadLocalMap(this, firstValue); }ThreadLocal$ThreadLocalMap. set(ThreadLocal key, Object value) 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; // 获取到threadLocal对应的索引 int i = key.threadLocalHashCode & (len-1); // 遍历i之后的entry 直到该entry为null // 如果当前entry.threadLocal和key是同一个对象 更新value // 如果当前entry.threadLocal为null[该threadLocal被gc了] replaceStaleEntry // 向后查找是否存在和当前threadLocal匹配的entry, 如果有则将其替换到当前位置 // 否则 则在当前位置创建一个entry // 遍历到了为null的entry 则设置entry 清理对象 // 如果没有清除任何对象 并且size超过了阈值 进行扩容 // 这里的三个出口对应着之前给出的插入数据的三幅图 for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal k = e.get(); if (k == key) { e.value = value; return; } if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }ThreadLocal$ThreadLocalMap. replaceStaleEntry(ThreadLocal key, Object value, int staleSlot) 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). // slotToExpunge表示在staleSlot前面的第一个为null的entry之后[staleSlot之前]的第一个key被gc的entry, 或者staleShlot[staleSlot到其前面的”第一个为空的entry”之间没有key被gc的entry] // 用于后面的expungeStaleEntry 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 // 从staleSlot开始后遍历 直到该entry为null for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal k = e.get(); // If we find key, then we need to swap it // with the stale entry to maintain hash table order. // 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. // 如果当前entry.threadLocal==key[表示key在当前ThreadLocalMap的位置在i处, 而现在的场景下面更好的位置在于slateSlot处[详见ThreadLocalMap.set] ]// 更新value 交换tab[i]和tab[staleSlot] // 交换之后tab[i].threadLocal为null[详见ThreadLocalMap.set], tab[staleSlot] = e if (k == key) { e.value = value; tab[i] = tab[staleSlot]; tab[staleSlot] = e; // Start expunge at preceding stale entry if it exists // 二者相等 表示staleSlot到其前面的”第一个为空的entry”之间没有key被gc的entry, 更新slotToExploge if (slotToExpunge == staleSlot) slotToExpunge = i; // 从slotToExpunge开始清理key被gc的entry 以及更新各个符合条件的entry的位置// 然后启发式的清理一些entrycleanSomeSlots(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. // 二者相等 表示staleSlot到其前面的”第一个为空的entry”之间没有key被gc的entry // 如果entry.threadLocal为null[表示第i个元素的key被gc掉了] 并且二者相等更新slotToExpunge if (k == null && slotToExpunge == staleSlot) slotToExpunge = i; } // If key not found, put new entry in stale slot // 如果在staleSlot之后没有找到和key匹配的entry // 则说明key对应的entry不在当前threadLocalMap中, 新建一个entry(key, value)到tab[staleSlot] tab[staleSlot].value = null; tab[staleSlot] = new Entry(key, value); // If there are any other stale entries in run, expunge them // 如果二者不相等 (表示staleSlot到其前面的”第一个为空的entry”之间存在key被gc的entry)或者 (staleSlot之后的与key不匹配的entry的key被gc了 并且(表示staleSlot到其前面的”第一个为空的entry”之间没有key被gc的entry) ) //从slotToExpunge开始清理key被gc的entry, 以及更新各个符合条件的entry的位置 // 然后启发式的清理一些entry if (slotToExpunge != staleSlot) cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); }ThreadLocal$ThreadLocalMap. cleanSomeSlots(int i, int n) private boolean cleanSomeSlots(int i, int n) { // 试图在i之后log2len步内 找到一个key被gc的entry清理 // 如果找到一个 则继续试图在i之后log2len步内, XXX boolean removed = false; Entry[] tab = table; int len = tab.length; // 遍历i之后的entry 如果该entry不为null且entry.threadLocal为null[key被gc的entry] // 更新n, removed 清理该entry do { i = nextIndex(i, len); Entry e = tab[i]; if (e != null && e.get() == null) { n = len; removed = true; i = expungeStaleEntry(i); } // n = n / 2 } while ( (n >>>= 1) != 0); return removed; }ThreadLocal$ThreadLocalMap. rehash() private void rehash() { expungeStaleEntries(); // Use lower threshold for doubling to avoid hysteresis if (size >= threshold - threshold / 4) resize(); }ThreadLocal$ThreadLocalMap. expungeStaleEntries() private void expungeStaleEntries() { Entry[] tab = table; int len = tab.length; // 遍历tab 寻找entry不为null 且entry.threadLocal为null的entry 进行清理 for (int j = 0; j < len; j++) { Entry e = tab[j]; if (e != null && e.get() == null) expungeStaleEntry(j); } }ThreadLocal$ThreadLocalMap. resize() private void resize() { Entry[] oldTab = table; int oldLen = oldTab.length; // 长度变为原来长度的2倍 新建Entry数组 int newLen = oldLen * 2; Entry[] newTab = new Entry[newLen]; int count = 0; // 遍历原来的tab 添加oldTab中的数据到newTab中 for (int j = 0; j < oldLen; ++j) { Entry e = oldTab[j]; if (e != null) { ThreadLocal k = e.get(); if (k == null) { e.value = null; // Help the GC } else { int h = k.threadLocalHashCode & (newLen - 1); // 获取索引 如果最合适的索引存在值 则访问下一个索引 知道找到下一个entry为null的entry while (newTab[h] != null) h = nextIndex(h, newLen); newTab[h] = e; count++; } } } // 更新threshold, size, tab setThreshold(newLen); size = count; table = newTab;ThreadLocal$ThreadLocalMap. setThreshold(int len) private void setThreshold(int len) { // load factor is 0.6666 threshold = len * 2 / 3; }
附上一张 ThreadLocal$ThreadLocalMap. replaceStaleEntry(ThreadLocal key, Object value, int staleSlot) 中staleSlot 和slotToExpunge 存在的三种关系的场景
ThreadLocal. remove()
public void remove() { // 获取当前线程的threadLocalMap 并移除当前threadLocal对象所对应的对象 ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }ThreadLocal$ThreadLocalMap. remove(ThreadLocal key) private void remove(ThreadLocal key) { Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); // 从i开始遍历 如果存在一个entry.threadLocal == key则清除该entry, 并从该位置开始, 清空key被gc的元素, 向前移动符合条件的非空元素, 直到碰到空元素 for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { if (e.get() == key) { e.clear(); expungeStaleEntry(i); return; } } }ThreadLocal$ThreadLocalMap$Entry. clear()public void clear() { // 清除当前entry.referent this.referent = null; }
从上面三个方法可以看出, 当在实现功能的同时, 还必需维护一个threadLocalMap的性质, 而这个性质钳制了下一次方法的执行
这个性质就是 : 当前threadLocal对应的entry必然在entry[hash(threadLocal) % len] 到下一个为null的entry之间
ThreadLocalMap [InnerClass]
threadLocalMap中的方法, 均是private的, 因此ThreadLocal就是为操作每一个线程的threadLocalMap中当前threadLocal对象对应的entry提供一个接口
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). 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. */ // 继承自WeakReference的Entry 封装了一个ThreadLocal, 以及一个线程的数据对象 static class Entry extends WeakReference<ThreadLocal> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } } // 容量以及, 当前threadLocalMap的entry表 // 当前数据的个数, 扩容的阈值 /** * The initial capacity -- MUST be a power of two. */ private static final int INITIAL_CAPACITY = 16; /** * The table, resized as necessary. * table.length MUST always be a power of two. */ private Entry[] table; /** * The number of entries in the table. */ private int size = 0; /** * The next size value at which to resize. */ private int threshold; // Default to 0 /** * Set the resize threshold to maintain at worst a 2/3 load factor. */ // 根据表的长度设置阈值 private void setThreshold(int len) { threshold = len * 2 / 3; } // 获取[上 / 下]一个索引, 循环结构 // 也就是对于nextIndex, 当i到达len-1的时候, i会跳转到0 // 同理对于prevIndex, 当i到达0的时候, i会跳转到len-1 /** * Increment i modulo len. */ private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); } /** * Decrement i modulo len. */ private static int prevIndex(int i, int len) { return ((i - 1 >= 0) ? i - 1 : len - 1); } /** * Construct a new map initially containing (firstKey, firstValue). * ThreadLocalMaps are constructed lazily, so we only create * one when we have at least one entry to put in it. */ // 带有第一个entry的初始化, 初始化entry表, 设置阈值 添加第一条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); } /** * Construct a new map including all Inheritable ThreadLocals * from given parent map. Called only by createInheritedMap. * * @param parentMap the map associated with parent thread. */ // 通过给定的threadLocalMap来构造当前ThreadLocalMap // 添加parentMap中所有的不为null 并且entry.threadLocal不为null 的entry // 这里相对于下面的set(ThreadLocal, Object)主要有两点不同, 所以这里只走了 set方法的最后一个出口 // 1. table不会存在重复的key // 2. 因为这里使用了if(key != null) 的判断, 所以过滤掉了key为被gc掉的threadLocal的entry 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) { ThreadLocal key = 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++; } } } } /** * Get the entry associated with key. This method * itself handles only the fast path: a direct hit of existing * key. It otherwise relays to getEntryAfterMiss. This is * designed to maximize performance for direct hits, in part * by making this method readily inlinable. * * @param key the thread local object * @return the entry associated with key, or null if no such */ // 略 ... 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); } /** * Version of getEntry method for use when key is not found in * its direct hash slot. * * @param key the thread local object * @param i the table index for key's hash code * @param e the entry at table[i] * @return the entry associated with key, or null if no such */ // 略 ... 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; } /** * Set the value associated with key. * * @param key the thread local object * @param value the value to be set */ // 略 ... 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(); if (k == key) { e.value = value; return; } if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } /** * Remove the entry for key. */ // 略 ... 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; } } } /** * Replace a stale entry encountered during a set operation * with an entry for the specified key. The value passed in * the value parameter is stored in the entry, whether or not * an entry already exists for the specified key. * * 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 the value to be associated with 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(); // If we find key, then we need to swap it // with the stale entry to maintain hash table order. // 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; // Start expunge at preceding stale entry if it exists 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; } // If key not found, put new entry in stale slot tab[staleSlot].value = null; tab[staleSlot] = new Entry(key, value); // If there are any other stale entries in run, expunge them if (slotToExpunge != staleSlot) cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); } /** * 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. See * 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; // expunge entry at staleSlot tab[staleSlot].value = null; tab[staleSlot] = null; size--; // Rehash until we encounter null Entry e; int i; for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal k = e.get(); if (k == null) { e.value = null; tab[i] = null; size--; } else { int h = k.threadLocalHashCode & (len - 1); if (h != i) { tab[i] = null; // Unlike Knuth 6.4 Algorithm R, we must scan until // null because multiple entries could have been stale. while (tab[h] != null) h = nextIndex(h, len); tab[h] = e; } } } return i; } /** * Heuristically scan some cells looking for stale entries. * This is invoked when either a new element is added, or * another stale one has been expunged. 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 a position known NOT to hold a stale entry. The * scan starts at the element after i. * * @param n scan control: <tt>log2(n)</tt> cells are scanned, * unless a stale entry is found, in which case * <tt>log2(table.length)-1</tt> 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. But this version is simple, fast, and * seems to work well.) * * @return true if any stale entries have been removed. */ // 略 ... 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 = expungeStaleEntry(i); } } while ( (n >>>= 1) != 0); return removed; } /** * Re-pack and/or re-size the table. First scan the entire * table removing stale entries. If this doesn't sufficiently * shrink the size of the table, double the table size. */ // 略 ... private void rehash() { expungeStaleEntries(); // Use lower threshold for doubling to avoid hysteresis if (size >= threshold - threshold / 4) resize(); } /** * Double the capacity of the table. */ // 略 ... private void resize() { Entry[] oldTab = table; int oldLen = oldTab.length; 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) { 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; } /** * Expunge all stale entries in the table. */ // 略 ... 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); } } }
->
end
参考 :
http://jerrypeng.me/2013/06/thread-local-and-magical-0x61c88647/?utm_source=tuicool
http://maosidiaoxian.iteye.com/blog/1939142
http://www.iteye.com/topic/103804
http://qifuguang.me/2015/09/02/[Java%E5%B9%B6%E5%8F%91%E5%8C%85%E5%AD%A6%E4%B9%A0%E4%B8%83]%E8%A7%A3%E5%AF%86ThreadLocal/
–2015.03.23
- 10 java.lang.ThreadLocal
- java.lang.ThreadLocal例子
- Java.lang.ThreadLocal类
- java.lang.ThreadLocal
- java.lang.ThreadLocal类
- 理解 java.lang.ThreadLocal
- java.lang.ThreadLocal类
- java.lang.ThreadLocal类
- java.lang.ThreadLocal类
- java.lang.ThreadLocal类
- java.lang.ThreadLocal类
- 关于java.lang.ThreadLocal
- java.lang.ThreadLocal
- java.lang.ThreadLocal
- java.lang.ThreadLocal类
- 研究java.lang.ThreadLocal类
- java.lang.ThreadLocal()的用法
- 深入研究java.lang.ThreadLocal
- jQuery的相关知识点
- require.js使用步骤
- linux使用flock文件锁解决脚本重复执行问题
- java spring quartz定时调度从入门到精通
- 我的进阶曲线之七
- 10 java.lang.ThreadLocal
- [IOS 开发] iOS 如何将日期字符串转成NSDate
- c# b/s 网站中截取网页图片
- python中的enumerate
- Spring Batch学习之路- 简介(一)
- 【JavaWeb探究】自定义JSP标签(二)
- hello world
- Oracle中单引号的使用
- 利用JDBC根据表结构生成ModelClass