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 ...
随机推荐
- IE6兼容笔记
1.IE6中,元素右浮动的时候前面不能有文本或内联元素,否则会换行独占一行 解决办法:将浮动元素放到文本或内联元素前面,大都在制作新闻列表的时候会遇到这种问题. 未完,待续!
- 推荐JavaScript动态效果库
翻译:疯狂的技术宅,原文:https://blog.bitsrc.io/11-javascript-animation-libraries-for-2018-9d7ac93a2c59 当我想要在网上找 ...
- MySQL下载~安装教程~这里示例 MySQL 8.0 Command Line Client
打开 https://www.mysql.com 也可以选择我分享的百度网盘文件 MySQL8.0.16.0 安装包 https://pan.baidu.com/s/1U8DkyJVp9Zvx7Zok ...
- Hibernate 最简单实例
我从网上下载了 hibernate-release-4.3.0.Final.zip,解压缩,把/lib/required文件夹下的所有jar包加入到eclipse项目中的Referenced Libr ...
- Delphi 字符串运算符
- 引用vector里的元素被删除后,引用会怎么样?
引用的定义不多说,直接看做变量的别名就可以了.有一天写着写着代码,突然想到,如果对vector里某个元素设置引用后,将这个元素从vector里删除会怎么样?我思考了下,认为那个元素会被删除,但是引用还 ...
- 第二章Python入门
第二章 Python入门 2.1.简介 Python是著名的"龟叔"(Guido van Rossum)在1989年圣诞节期间,为了打发无聊的圣诞节而编写的一个编程语言 Pytho ...
- R语言-八皇后问题
老师给我出了个暑期作业:用R语言解决八皇后问题. 八皇后问题:国际象棋棋盘(8×8)上放8个“后”,使8个“后”之间互相不能被进攻.(即:每个“后”所在行.列.两条斜线都没有其它子) 查看网上,大多用 ...
- VueJS简明教程(一)之基本使用方法
简介:这是一篇超级简单的入门文章 如果说是JQuery是手工作坊,那么Vue.js就像是一座工厂,虽然Vue.js做的任何事情JQuery都可以做,但无论是代码量还是流程规范性都是前者较优. Vue. ...
- Redis 历史版本下载URL
Redis 历史版本下载URL: http://download.redis.io/releases/ Redis和RedisClient 官网下载方式: https://blog.51cto.com ...