ThreadLocal 源码分析
线程局部变量
ThreadLocal 用于实现线程隔离和类间变量共享。
创建实例
/**
* 当前 ThreadLocal 实例的哈希值
*/
private final int threadLocalHashCode = nextHashCode();
/**
* 下一个 ThreadLocal 实例的哈希值
*/
private static AtomicInteger nextHashCode =
new AtomicInteger();
/**
* 下一个 ThreadLocal 实例的哈希值增量,可以最大程度地避免碰撞
*/
private static final int HASH_INCREMENT = 0x61c88647;
/**
* 读取下一个哈希值
*/
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
/**
* 创建一个线程局部变量
*/
public ThreadLocal() {
}
写入
/**
* 将目标值 value 写入此 ThreadLocal 变量中
*/
public void set(T value) {
// 读取当前线程
final Thread t = Thread.currentThread();
// 读取与当前线程绑定的 ThreadLocalMap
final ThreadLocalMap map = getMap(t);
// 1)ThreadLocalMap 已经存在,则直接写入
if (map != null) {
map.set(this, value);
} else {
// 2)创建 ThreadLocalMap 并写入
createMap(t, value);
}
}
读取
/**
* 读取此 ThreadLocal 实例关联的值
*/
public T get() {
final Thread t = Thread.currentThread();
final ThreadLocalMap map = getMap(t);
if (map != null) {
// 读取 Entry
final ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
final
// 读取值
T result = (T)e.value;
return result;
}
}
// 此 ThreadLocal 还未初始化,则写入初始值
return setInitialValue();
}
移除
/**
* 移除此 ThreadLocal
*/
public void remove() {
final ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null) {
m.remove(this);
}
}
ThreadLocalMap
static class ThreadLocalMap {
/**
* ThreadLocalMap 的 Entry 继承了 WeakReference,以便能处理大量的条目,
* 当 entry.get()==null 时,表示关联的 ThreadLocal 对象已经被回收,该条目
* 可以从此 ThreadLocalMap 中移除了
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** 与 ThreadLocal 关联的值 */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
/**
* table 的初始容量
*/
private static final int INITIAL_CAPACITY = 16;
/**
* 容纳键值对的底层 table
*/
private Entry[] table;
/**
* table 中的条目数
*/
private int size = 0;
/**
* 下一次扩容的阈值
*/
private int threshold; // Default to 0
/**
* 写入下次扩容的阈值,table 容量的 2/3
*/
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
/**
* 索引递增 1
*/
private static int nextIndex(int i, int len) {
return i + 1 < len ? i + 1 : 0;
}
/**
* 索引递减 1
*/
private static int prevIndex(int i, int len) {
return i - 1 >= 0 ? i - 1 : len - 1;
}
/**
* 创建 ThreadLocalMap 实例,并写入第一个 entry
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
// 创建初始容量为 16 的 table
table = new Entry[INITIAL_CAPACITY];
// 第一个键的 threadLocalHashCode 为 0,即目标索引也为 0
final int i = firstKey.threadLocalHashCode & INITIAL_CAPACITY - 1;
// 创建并写入 Entry
table[i] = new Entry(firstKey, firstValue);
size = 1;
// 写入阈值,table 容量的 2/3
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.
*/
private ThreadLocalMap(ThreadLocalMap parentMap) {
final Entry[] parentTable = parentMap.table;
final int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (final Entry e : parentTable) {
if (e != null) {
@SuppressWarnings("unchecked")
final
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
final Object value = key.childValue(e.value);
final 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
*/
private Entry getEntry(ThreadLocal<?> key) {
// 计算索引值
final int i = key.threadLocalHashCode & table.length - 1;
final Entry e = table[i];
// 读取 Entry 的键和目标 key 相等,则直接返回
if (e != null && e.get() == key) {
return e;
} else {
// 往后查找 Entry
return getEntryAfterMiss(key, i, e);
}
}
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
final Entry[] tab = table;
final int len = tab.length;
while (e != null) {
final ThreadLocal<?> k = e.get();
// 当前 Entry 的键和目标 key 相等,则返回
if (k == key) {
return e;
}
// 1)当前弱键已经被回收,则移除此 Entry
if (k == null) {
expungeStaleEntry(i);
} else {
// 2)索引值递增 1,循环查找
i = nextIndex(i, len);
}
// 定位下一个 Entry
e = tab[i];
}
// 未找到则返回 null
return null;
}
/**
* 将指定的 ThreadLocal 键值对写入此 ThreadLocalMap 中
*/
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.
*/
// 读取 table
final Entry[] tab = table;
// 读取 length
final int len = tab.length;
// 基于 ThreadLocal 的哈希值计算索引
int i = key.threadLocalHashCode & len-1;
/**
* 读取目标索引处的 Entry && 此 Entry 不为 null
* 1)如果当前 Entry 不匹配,则循环查找下一个 Entry,直到下一个 Entry 为空
*/
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
// 读取 Entry 的 ThreadLocal
final ThreadLocal<?> k = e.get();
// 当前 ThreadLocal 与目标 ThreadLocal 相等,则更新其值
if (k == key) {
e.value = value;
return;
}
// 如果弱键已经被回收,则移除过时的 Entry
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
// 未找到,则创建并写入新的 Entry
tab[i] = new Entry(key, value);
// 递增 size
final int sz = ++size;
/**
* 1)尝试清除索引 i 之后的一些 slot,如果清除成功,则此 table 无需扩容
* 2)slot 清除失败 && 判断当前总元素数是否超出扩容阈值 && 超出则进行扩容
*/
if (!cleanSomeSlots(i, sz) && sz >= threshold) {
rehash();
}
}
/**
* 移除指定 key 关联的 Entry
*/
private void remove(ThreadLocal<?> key) {
final Entry[] tab = table;
final 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();
// 清除此 Entry
expungeStaleEntry(i);
return;
}
}
}
private void replaceStaleEntry(ThreadLocal<?> key, Object value,
int staleSlot) {
final Entry[] tab = table;
final int len = tab.length;
Entry e;
/**
* 循环查找过时的 Entry,并尝试更新索引,直到遇到一个空 slot 为止
*/
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)) {
final 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);
}
}
private int expungeStaleEntry(int staleSlot) {
final Entry[] tab = table;
final int len = tab.length;
// 移除 staleSlot 索引处 ThreadLocal 关联的值
tab[staleSlot].value = null;
// 移除 staleSlot 的 Entry
tab[staleSlot] = null;
// 递减 size
size--;
// Rehash until we encounter null
Entry e;
int i;
// 计算被异常条目之后的 index && 定位的 Entry 不为 null
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
// 读取键
final ThreadLocal<?> k = e.get();
// 此 Entry 关联的弱键已经被回收
if (k == null) {
// 清除值
e.value = null;
// 清除 Entry
tab[i] = null;
// 递减 size
size--;
} else {
// 计算索引
int h = k.threadLocalHashCode & len - 1;
// 如果 table 已经扩容
if (h != i) {
// 将旧的 Entry 置为 null
tab[i] = null;
// 从新的索引位置开始寻找一个空的 slot
while (tab[h] != null) {
h = nextIndex(h, len);
}
// 写入 Entry
tab[h] = e;
}
}
}
return i;
}
private boolean cleanSomeSlots(int i, int n) {
boolean removed = false;
final Entry[] tab = table;
final int len = tab.length;
do {
// 计算下一个索引
i = nextIndex(i, len);
// 读取 Entry
final Entry e = tab[i];
// 如果此 Entry 条目不为空 && 条目关联的值为 null
if (e != null && e.get() == null) {
n = len;
removed = true;
// 移除过时的 Entry
i = expungeStaleEntry(i);
}
// 将 n 减半
} while ( (n >>>= 1) != 0);
return removed;
}
private void rehash() {
expungeStaleEntries();
// 当前元素总数 >= 阈值的 3/4
if (size >= threshold - threshold / 4) {
resize();
}
}
/**
* 双倍扩容
*/
private void resize() {
final Entry[] oldTab = table;
final int oldLen = oldTab.length;
final int newLen = oldLen * 2;
final Entry[] newTab = new Entry[newLen];
int count = 0;
for (final Entry e : oldTab) {
if (e != null) {
final ThreadLocal<?> k = e.get();
// 1)弱键已经被移除,则清空其值
if (k == null) {
e.value = null; // Help the GC
} else {
// 2)基于当前 ThreadLocal 的哈希值定位索引,顺序找到第一个可用的 slot
int h = k.threadLocalHashCode & newLen - 1;
while (newTab[h] != null) {
h = nextIndex(h, newLen);
}
// 写入 Entry
newTab[h] = e;
// 递增元素数
count++;
}
}
}
setThreshold(newLen);
size = count;
table = newTab;
}
/**
* 清除所有过时的 Entry
*/
private void expungeStaleEntries() {
final Entry[] tab = table;
final int len = tab.length;
for (int j = 0; j < len; j++) {
final Entry e = tab[j];
if (e != null && e.get() == null) {
expungeStaleEntry(j);
}
}
}
}
ThreadLocal 源码分析的更多相关文章
- Java多线程学习之ThreadLocal源码分析
0.概述 ThreadLocal,即线程本地变量,是一个以ThreadLocal对象为键.任意对象为值的存储结构.它可以将变量绑定到特定的线程上,使每个线程都拥有改变量的一个拷贝,各线程相同变量间互不 ...
- Java并发编程之ThreadLocal源码分析
## 1 一句话概括ThreadLocal<font face="微软雅黑" size=4> 什么是ThreadLocal?顾名思义:线程本地变量,它为每个使用该对象 ...
- 并发编程(四)—— ThreadLocal源码分析及内存泄露预防
今天我们一起探讨下ThreadLocal的实现原理和源码分析.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码分析了其实现原理和使用需要注意的地方,最后给出了两 ...
- 【JAVA】ThreadLocal源码分析
ThreadLocal内部是用一张哈希表来存储: static class ThreadLocalMap { static class Entry extends WeakReference<T ...
- 并发-ThreadLocal源码分析
ThreadLocal源码分析 参考: http://www.cnblogs.com/dolphin0520/p/3920407.html https://www.cnblogs.com/coshah ...
- ThreadLocal源码分析-黄金分割数的使用
前提 最近接触到的一个项目要兼容新老系统,最终采用了ThreadLocal(实际上用的是InheritableThreadLocal)用于在子线程获取父线程中共享的变量.问题是解决了,但是后来发现对T ...
- ThreadLocal详解,ThreadLocal源码分析,ThreadLocal图解
本文脉路: 概念阐释 ----> 原理图解 ------> 源码分析 ------> 思路整理 ----> 其他补充. 一.概念阐述. ThreadLocal 是一个为 ...
- Java -- 基于JDK1.8的ThreadLocal源码分析
1,最近在做一个需求的时候需要对外部暴露一个值得应用 ,一般来说直接写个单例,将这个成员变量的值暴露出去就ok了,但是当时突然灵机一动(现在回想是个多余的想法),想到handle源码里面有使用过Th ...
- Java核心复习—— ThreadLocal源码分析
ThreadLocal,叫做线程本地存储,也可以叫做线程本地变量.ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量. 一.如何使用 class Acce ...
- ThreadLocal源码分析与实践
ThreadLocal是什么? ThreadLocal是一个线程内部存储类,提供线程内部存储功能,在一个ThreadLocal对象中,每一个线程都存储各自独立的数据,互不干扰 示例如下: public ...
随机推荐
- 4种vue当中的指令和它的用法
1.v-if:判断是否隐藏 2.v-for:数据循环 3.v-bind:class:绑定一个属性 4.v-model:实现数据双向绑定 这里重点说明一个v-if和v-show的区别: 共同点:都是通过 ...
- decodeURI decodeURIComponent
操作 url 常用到编码与解码,一一对应就好 给力文章
- MATLAB仿真 让波形动起来
dt=1e-6;T=2*1e-3;for N=0:500; t=N*T+(0:dt:T); input=2*cos(2*pi*1005*t); carrier=5*cos(2*pi*(1e4)*t+0 ...
- bootloader架构设计
G-boot架构设计 第一阶段程序设计 1.0.核心初始化: 1.设置中断向量表 2.设置处理器为svc模式 3.关闭看门狗 4.关闭所有中断 5.关闭mmu和cache 6.外设基地址初始化 ...
- nginx的代理服务
nginx的代理服务 正向代理和反向代理 正向代理服务器就是用来让局域网的客户端接入外网访问外网资源,反向代理就是让外网的客户端接入局域网中的站点以访问点中的资源 正向代理 我是一个用户,我访问不了某 ...
- js 判断确切判断Array和Object
js的数组其实是特殊的对象. 这就导致: typeof [1,2,3] === 'object' [1,2,3] instanceof Object 和 [1,2,3] instanceof Arr ...
- [每日一学]apache camel简介
apache camel 是轻量级esb框架.如下是它的架构图: 它有几个比较重要的概念就是: 1.endpoint,所谓的endpoint,就是一种可以接收或发送数据的组件.可以支持多种协议,如jm ...
- 【LeetCode】堆 heap(共31题)
链接:https://leetcode.com/tag/heap/ [23] Merge k Sorted Lists [215] Kth Largest Element in an Array (无 ...
- Hadoop中配置环境后重启失效解决方法
Ubuntu下设置环境变量有三种方法,一种用于当前终端,一种用于当前用户,一种用于所有用户: 一:用于当前终端: 在当前终端中输入:export PATH=$PATH:<路径> 不过上面的 ...
- Spring 跨重定向请求传递数据
在处理完POST请求后, 通常来讲一个最佳实践就是执行一下重定向.除了其他的一些因素外,这样做能够防止用户点击浏览器的刷新按钮或后退箭头时,客户端重新执行危险的POST请求. 在控制器方法返回的视图名 ...