Java源码解析ThreadLocal
Posted 林城画序
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java源码解析ThreadLocal相关的知识,希望对你有一定的参考价值。
简介
线程本地变量,用于同一线程之间的传递。每一个线程对象都保存在两个ThreadLocalMap,threadLocals和inheritableThreadLocals,后者会继承父线程的本地变量,以ThreadLocal对象为key,取得map里的值。
源码
属性和构造方法
1 // 哈希值 2 private final int threadLocalHashCode = nextHashCode(); 3 4 private static AtomicInteger nextHashCode = 5 new AtomicInteger(); 6 7 private static final int HASH_INCREMENT = 0x61c88647; 8 9 private static int nextHashCode() { 10 return nextHashCode.getAndAdd(HASH_INCREMENT); 11 } 12 13 protected T initialValue() { // 初始值为空 14 return null; 15 } 16 17 public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) { // 函数式编程,返回一个ThreadLocal对象 18 return new SuppliedThreadLocal<>(supplier); 19 } 20 21 public ThreadLocal() { // 构造方法 22 }
SuppliedThreadLocal
1 static final class SuppliedThreadLocal<T> extends ThreadLocal<T> { 2 3 private final Supplier<? extends T> supplier; 4 5 SuppliedThreadLocal(Supplier<? extends T> supplier) { 6 this.supplier = Objects.requireNonNull(supplier); 7 } 8 9 @Override 10 protected T initialValue() { 11 return supplier.get(); 12 } 13 }
基本方法
get()
1 public T get() { 2 Thread t = Thread.currentThread(); // 获取当前线程 3 ThreadLocalMap map = getMap(t); // 根据当前线程获取ThreadLocalMap, Thread#threadLocals,子类可重写getMap()方法,比如InheritableThreadLocal, 返回的就是Thread#inheritableThreadLocals 4 if (map != null) { 5 ThreadLocalMap.Entry e = map.getEntry(this); // 以当前ThreadLocal对象为key, 取得value(ThreadLocalMap.Entry) 6 if (e != null) { 7 @SuppressWarnings("unchecked") 8 T result = (T)e.value; // 返回结果 9 return result; 10 } 11 } 12 return setInitialValue(); // 如果map为空,执行初始化 13 }
getMap()
1 ThreadLocalMap getMap(Thread t) { // 获得map 2 return t.threadLocals; 3 }
setInitialValue()
1 private T setInitialValue() { // 设置初始值 2 T value = initialValue(); // 取得初始值 3 Thread t = Thread.currentThread(); // 当前线程 4 ThreadLocalMap map = getMap(t); // 获取map 5 if (map != null) // 不为空,设置value, key为当前对象 6 map.set(this, value); 7 else 8 createMap(t, value); // 否则,创建map 9 return value; 10 }
set()
1 public void set(T value) { // 设置值,逻辑同setInitialValue() 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 }
创建map(ThreadLocalMap)
1 void createMap(Thread t, T firstValue) { // 创建map(ThreadLocalMap) 2 t.threadLocals = new ThreadLocalMap(this, firstValue); 3 } 4 5 static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { // 继承map值 6 return new ThreadLocalMap(parentMap); 7 }
ThreadLocalMap
属性
1 static class Entry extends WeakReference<ThreadLocal<?>> { // 弱引用 2 Object value; 3 4 Entry(ThreadLocal<?> k, Object v) { 5 super(k); 6 value = v; 7 } 8 } 9 10 private static final int INITIAL_CAPACITY = 16; // 初始容量 11 12 private Entry[] table; // 数组 13 14 private int size = 0; // 大小 15 16 private int threshold; // 阈值,长度的2/3 17 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); // 长度范围内,加1; 超过范围,0 24 } 25 26 private static int prevIndex(int i, int len) { // 前一个索引 27 return ((i - 1 >= 0) ? i - 1 : len - 1); // 长度范围内,减1; 超过范围,len - 1 28 }
构造方法
1 ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { // 构造方法 2 table = new Entry[INITIAL_CAPACITY]; // 初始化数组,初始容量为16 3 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); // firstKey的索引 4 table[i] = new Entry(firstKey, firstValue); // 构造Entry(firstKey->firstValue)对象,并置于i索引处 5 size = 1; // 当前大小为1 6 setThreshold(INITIAL_CAPACITY); // 设置阈值 7 } 8 9 private ThreadLocalMap(ThreadLocalMap parentMap) { // 以父ThreadLocalMap构造ThreadLocalMap 10 Entry[] parentTable = parentMap.table; // 取得parentMap的table 11 int len = parentTable.length; // 取得table长度 12 setThreshold(len); // 设置阈值 13 table = new Entry[len]; // 创建数组table 14 15 for (int j = 0; j < len; j++) { 16 Entry e = parentTable[j]; 17 if (e != null) { 18 @SuppressWarnings("unchecked") 19 ThreadLocal<Object> key = (ThreadLocal<Object>) e.get(); // 取得key 20 if (key != null) { 21 Object value = key.childValue(e.value); // 计算子线程的value 22 Entry c = new Entry(key, value); // 构建Entry对象 23 int h = key.threadLocalHashCode & (len - 1); // 计算索引 24 while (table[h] != null) // 如果索引不为空,则计算下一个索引,直到找到空位 25 h = nextIndex(h, len); // 寻找下一个索引,(hash碰撞时,没有使用链表,而是寻找下一个索引) 26 table[h] = c; 27 size++; // 长度加1 28 } 29 } 30 } 31 }
基本方法
getEntry()
1 private Entry getEntry(ThreadLocal<?> key) { // 获取entry 2 int i = key.threadLocalHashCode & (table.length - 1); // 计算索引 3 Entry e = table[i]; // 取得entry 4 if (e != null && e.get() == key) // 找到返回 5 return e; 6 else 7 return getEntryAfterMiss(key, i, e); // 否则调用getEntryAfterMiss方法 8 }
getEntryAfterMiss()
1 private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { // 处理碰撞的情况 2 Entry[] tab = table; 3 int len = tab.length; 4 5 while (e != null) { // 若e为空,直接返回null, 否则遍历table 6 ThreadLocal<?> k = e.get(); // 获得key 7 if (k == key) // 若相等,则找到返回 8 return e; 9 if (k == null) 10 expungeStaleEntry(i); // 删除过期的Entry对象 11 else 12 i = nextIndex(i, len); // 计算下一个索引,继续寻找 13 e = tab[i]; 14 } 15 return null; 16 }
expungeStaleEntry()
1 private int expungeStaleEntry(int staleSlot) { 2 Entry[] tab = table; 3 int len = tab.length; 4 5 tab[staleSlot].value = null; // value置为空 6 tab[staleSlot] = null; // 槽位置为空 7 size--; // size减1 8 9 Entry e; 10 int i; 11 for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { // 以staleSlot起始,索引与之碰撞的所有槽位,尝试清除无效的元素 12 ThreadLocal<?> k = e.get(); // key 13 if (k == null) { // 过期,清理 14 e.value = null; 15 tab[i] = null; 16 size--; 17 } else { 18 int h = k.threadLocalHashCode & (len - 1); // 重新归置元素 19 if (h != i) { 20 tab[i] = null; // 原来的槽位清空 21 while (tab[h] != null) // 以h为始,找空位 22 h = nextIndex(h, len); 23 tab[h] = e; // 设置元素 24 } 25 } 26 } 27 return i; 28 }
set()
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]; e != null; e = tab[i = nextIndex(i, len)]) { 6 ThreadLocal<?> k = e.get(); 7 if (k == key) { // 命中,更新value, 返回 8 e.value = value; 9 return; 10 } 11 if (k == null) { // 替换过期的槽位 12 replaceStaleEntry(key, value, i); 13 return; 14 } 15 } 16 tab[i] = new Entry(key, value); // 找到空位,新建Entry对象 17 int sz = ++size; // size加1 18 if (!cleanSomeSlots(i, sz) && sz >= threshold) // 清理槽位失败,并且当前size大于阈值,调用rehash方法 19 rehash(); 20 }
replaceStaleEntry()
1 private void replaceStaleEntry(ThreadLocal<?> key, Object value, int staleSlot) { 2 Entry[] tab = table; 3 int len = tab.length; 4 Entry e; 5 6 int slotToExpunge = staleSlot; 7 for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len)) // 从当前staleSlot往前找 8 if (e.get() == null) 9 slotToExpunge = i; // 过期槽位起始处,接下来从slotToExpunge清理过期槽位 10 11 for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { // 从当前staleSlot往后找 12 ThreadLocal<?> k = e.get(); 13 if (k == key) { // 命中,替换 14 e.value = value; // 替换value 15 tab[i] = tab[staleSlot]; // 交换i和staleSlot的元素,i槽位处等待被清理 16 tab[staleSlot] = e; 17 if (slotToExpunge == staleSlot) // 在staleSlot槽位之前没有过期的槽位,将slotToExpunge设置为i(staleSlot之后的槽位,因为staleSlot已经设置了有效的元素) 18 slotToExpunge = i; 19 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); // 清理工作 20 return; 21 } 22 if (k == null && slotToExpunge == staleSlot) 23 slotToExpunge = i; // 在staleSlot槽位之前没有过期的槽位,将slotToExpunge设置为i(staleSlot之后的槽位,因为staleSlot后面会设置有效的元素) 24 } 25 tab[staleSlot].value = null; // 置空 26 tab[staleSlot] = new Entry(key, value); // 设置新的值 27 if (slotToExpunge != staleSlot) // 如果有过期元素,做清理工作 28 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); 29 }
cleanSomeSlots()
1 private boolean cleanSomeSlots(int i, int n) { 2 boolean removed = false; 3 Entry[] tab = table; 4 int len = tab.length; 5 do { 6 i = nextIndex(i, len); 7 Entry e = tab[i]; 8 if (e != null && e.get() == null) { // 找到过期元素,执行清理操作 9 n = len; 10 removed = true; 11 i = expungeStaleEntry(i); // 具体操作还是由expungeStaleEntry完成 12 } 13 } while ((n >>>= 1) != 0); 14 return removed; 15 }
rehash()
1 private void rehash() { 2 expungeStaleEntries(); // 清理过期的元素 3 if (size >= threshold - threshold / 4) 4 resize(); // 扩容 5 }
expungeStaleEntries()
1 private void expungeStaleEntries() { 2 Entry[] tab = table; 3 int len = tab.length; 4 for (int j = 0; j < len; j++) { // 从槽位0处,尝试清理过期的条目 5 Entry e = tab[j]; 6 if (e != null && e.get() == null) 7 expungeStaleEntry(j); // 调用expungeStaleEntry方法 8 } 9 }
resize()
1 private void resize() { // 扩容 2 Entry[] oldTab = table; 3 int oldLen = oldTab.length; 4 int newLen = oldLen * 2; // 2倍 5 Entry[] newTab = new Entry[newLen]; // 新数组 6 int count = 0; 7 for (int j = 0; j < oldLen; ++j) { 8 Entry e = oldTab[j]; 9 if (e != null) { 10 ThreadLocal<?> k = e.get(); 11 if (k == null) { 12 e.value = null; // 帮助GC 13 } else { 14 int h = k.threadLocalHashCode & (newLen - 1); // rehash 15 while (newTab[h] != null) // 碰撞,计算下一个索引(槽位) 16 h = nextIndex(h, newLen); 17 newTab[h] = e; 18 count++; 19 } 20 } 21 } 22 23 setThreshold(newLen); // 设置新的阈值 24 size = count; 25 table = newTab; 26 }
remove()
1 private void remove(ThreadLocal<?> key) { 2 Entry[] tab = table; 3 int len = tab.length; 4 int i = key.threadLocalHashCode & (len - 1); 5 for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { 6 if (e.get() == key) { 7 e.clear(); // 移除 8 expungeStaleEntry(i); // 并清理过期槽位 9 return; 10 } 11 } 12 }
行文至此结束。
尊重他人的劳动,转载请注明出处:http://www.cnblogs.com/aniao/p/aniao_threadlocal.html
以上是关于Java源码解析ThreadLocal的主要内容,如果未能解决你的问题,请参考以下文章