博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Java源码解析】ThreadLocal
阅读量:4347 次
发布时间:2019-06-07

本文共 13107 字,大约阅读时间需要 43 分钟。

简介

线程本地变量,用于同一线程之间的传递。每一个线程对象都保存在两个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  ThreadLocal withInitial(Supplier
supplier) { // 函数式编程,返回一个ThreadLocal对象18 return new SuppliedThreadLocal<>(supplier);19 }20 21 public ThreadLocal() { // 构造方法22 }

 

SuppliedThreadLocal

1     static final class SuppliedThreadLocal
extends ThreadLocal
{ 2 3 private final Supplier
supplier; 4 5 SuppliedThreadLocal(Supplier
supplier) { 6 this.supplier = Objects.requireNonNull(supplier); 7 } 8 9 @Override10 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) { // 获得map2         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         else7             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
> { // 弱引用 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/317 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; 超过范围,024 }25 26 private static int prevIndex(int i, int len) { // 前一个索引27 return ((i - 1 >= 0) ? i - 1 : len - 1); // 长度范围内,减1; 超过范围,len - 128 }

 

构造方法

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构造ThreadLocalMap10 Entry[] parentTable = parentMap.table; // 取得parentMap的table11 int len = parentTable.length; // 取得table长度12 setThreshold(len); // 设置阈值13 table = new Entry[len]; // 创建数组table14 15 for (int j = 0; j < len; j++) {16 Entry e = parentTable[j];17 if (e != null) {18 @SuppressWarnings("unchecked")19 ThreadLocal key = (ThreadLocal) e.get(); // 取得key20 if (key != null) {21 Object value = key.childValue(e.value); // 计算子线程的value22 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++; // 长度加128 }29 }30 }31 }

 

基本方法

getEntry()

1         private Entry getEntry(ThreadLocal
key) { // 获取entry2 int i = key.threadLocalHashCode & (table.length - 1); // 计算索引3 Entry e = table[i]; // 取得entry4 if (e != null && e.get() == key) // 找到返回5 return e;6 else7 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 else12 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(); // key13 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加118 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; // 替换value15 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; // 帮助GC13 } else {14 int h = k.threadLocalHashCode & (newLen - 1); // rehash15 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

转载于:https://www.cnblogs.com/aniao/p/aniao_threadlocal.html

你可能感兴趣的文章
端口号大全
查看>>
POJ 3740 Easy Finding (DLX模板)
查看>>
MySQL 处理重复数据
查看>>
关于typedef的用法总结(转)
查看>>
Linux下安装rabbitmq
查看>>
曹德旺
查看>>
【转】判断点在多边形内(matlab)
查看>>
java基础之集合:List Set Map的概述以及使用场景
查看>>
Python 线程 进程 协程
查看>>
iOS语言中的KVO机制
查看>>
excel第一次打开报错 向程序发送命令时出错 多种解决办法含终极解决方法
查看>>
响应式web设计之CSS3 Media Queries
查看>>
实验三
查看>>
机器码和字节码
查看>>
环形菜单的实现
查看>>
【解决Chrome浏览器和IE浏览器上传附件兼容的问题 -- Chrome关闭flash后,uploadify插件不可用的解决办法】...
查看>>
34 帧动画
查看>>
二次剩余及欧拉准则
查看>>
thymeleaf 自定义标签
查看>>
关于WordCount的作业
查看>>