线程局部变量

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 源码分析的更多相关文章

  1. Java多线程学习之ThreadLocal源码分析

    0.概述 ThreadLocal,即线程本地变量,是一个以ThreadLocal对象为键.任意对象为值的存储结构.它可以将变量绑定到特定的线程上,使每个线程都拥有改变量的一个拷贝,各线程相同变量间互不 ...

  2. Java并发编程之ThreadLocal源码分析

    ## 1 一句话概括ThreadLocal<font face="微软雅黑" size=4>  什么是ThreadLocal?顾名思义:线程本地变量,它为每个使用该对象 ...

  3. 并发编程(四)—— ThreadLocal源码分析及内存泄露预防

    今天我们一起探讨下ThreadLocal的实现原理和源码分析.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码分析了其实现原理和使用需要注意的地方,最后给出了两 ...

  4. 【JAVA】ThreadLocal源码分析

    ThreadLocal内部是用一张哈希表来存储: static class ThreadLocalMap { static class Entry extends WeakReference<T ...

  5. 并发-ThreadLocal源码分析

    ThreadLocal源码分析 参考: http://www.cnblogs.com/dolphin0520/p/3920407.html https://www.cnblogs.com/coshah ...

  6. ThreadLocal源码分析-黄金分割数的使用

    前提 最近接触到的一个项目要兼容新老系统,最终采用了ThreadLocal(实际上用的是InheritableThreadLocal)用于在子线程获取父线程中共享的变量.问题是解决了,但是后来发现对T ...

  7. ThreadLocal详解,ThreadLocal源码分析,ThreadLocal图解

    本文脉路: 概念阐释 ---->  原理图解  ------> 源码分析 ------>  思路整理  ----> 其他补充. 一.概念阐述. ThreadLocal 是一个为 ...

  8. Java -- 基于JDK1.8的ThreadLocal源码分析

    1,最近在做一个需求的时候需要对外部暴露一个值得应用  ,一般来说直接写个单例,将这个成员变量的值暴露出去就ok了,但是当时突然灵机一动(现在回想是个多余的想法),想到handle源码里面有使用过Th ...

  9. Java核心复习—— ThreadLocal源码分析

    ThreadLocal,叫做线程本地存储,也可以叫做线程本地变量.ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量. 一.如何使用 class Acce ...

  10. ThreadLocal源码分析与实践

    ThreadLocal是什么? ThreadLocal是一个线程内部存储类,提供线程内部存储功能,在一个ThreadLocal对象中,每一个线程都存储各自独立的数据,互不干扰 示例如下: public ...

随机推荐

  1. python中逐行打印

    方法一:readline函数 f = open("./code.txt") # 返回一个文件对象 line = f.readline() # 调用文件的 readline()方法 ...

  2. easyui,获取tabs标签页内容,以及刷新datagrid

    因为先点开tab页xxx查看表格,再点另一个tab页xxxx修改部分信息,再切换到tab页xxx,tab页xxx里的datagrid表格不会刷新,显示不了修改的信息(在此tab页按F5刷新可以解决,但 ...

  3. 从零开始学MySQL(一)

    如果您浏览过许多IT类的招聘职位,那么掌握数据库技能的重要性便不言自明了.因JAVA常常与MySQL所挂钩,因此兴起了写博客记载学习的念头,盼与君共勉.然,在进行MySQL语法的学习之前,我们无不面临 ...

  4. Java引用与C语言指针的区别

    1.现象 指针在运行时可以改变其所指向的值(地址)即指向其它变量,而引用一旦和某个对象绑定后就不能再改变,总是指向最初的对象. 2.编译 程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变 ...

  5. Nagios-Nagios-Nagios系统监控(centos7部署源码)

    一.Nagios简介 Nagios是一款开源的电脑系统和网络监视工具,能有效监控Windows.Linux和Unix的主机状态,交换机路由器等网络设置,打印机等.在系统或服务状态异常时发出邮件或短信报 ...

  6. 浅谈java虚拟机|系列2|JVM运行时

    今天我们继续谈谈JVM架构. 今天主要讲讲JVM运行时, 先来一个图: 上篇文章,我们知道,JVM运行时,简单来说就是把class文件翻译成操作系统相关的机器码(或汇编语言),然后通过调用操作系统函数 ...

  7. (极值点偏移问题的几种方案)已知$\dfrac{\ln x_1}{x_1}=\dfrac{\ln x_2}{x_2}$,求证:$x_1+x_2>2\text{e}$.

    第一个图适合在手机上操作(点击\(\checkmark\)显示/隐藏) 第二个图适合在电脑上操作(点击\(\checkmark\)显示/隐藏)

  8. 在vue下引入jquery bootstrap

    在vue 项目中引入jquery bootstrap 引入jquery npm install jquery --save-dev 在项目根目录下的build/webpack.base.conf.js ...

  9. 【UOJ#394】[NOI2018] 冒泡排序

    题目链接 题意 求有多少个字典序严格大于给定排列 \(q_i\) 的排列满足其逆序对数(冒泡排序需要交换的次数)达到下限 \(\frac{1}{2}\sum_{i=1}^n |i-p_i|\) Sol ...

  10. pt-align的用法简要记录

    pt-align的用法简要记录 1.pt-align 功能:将其它工具的输出按列对齐用法:pt-align [FILES]如果没有指定文件,则默认读取标准输入的内容. 2.例如: [root@dbte ...