【Java源码解析】ThreadLocal
简介
线程本地变量,用于同一线程之间的传递。每一个线程对象都保存在两个ThreadLocalMap,threadLocals和inheritableThreadLocals,后者会继承父线程的本地变量,以ThreadLocal对象为key,取得map里的值。
源码
属性和构造方法
- // 哈希值
- private final int threadLocalHashCode = nextHashCode();
- private static AtomicInteger nextHashCode =
- new AtomicInteger();
- private static final int HASH_INCREMENT = 0x61c88647;
- private static int nextHashCode() {
- return nextHashCode.getAndAdd(HASH_INCREMENT);
- }
- protected T initialValue() { // 初始值为空
- return null;
- }
- public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) { // 函数式编程,返回一个ThreadLocal对象
- return new SuppliedThreadLocal<>(supplier);
- }
- public ThreadLocal() { // 构造方法
- }
SuppliedThreadLocal
- static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
- private final Supplier<? extends T> supplier;
- SuppliedThreadLocal(Supplier<? extends T> supplier) {
- this.supplier = Objects.requireNonNull(supplier);
- }
- @Override
- protected T initialValue() {
- return supplier.get();
- }
- }
基本方法
get()
- public T get() {
- Thread t = Thread.currentThread(); // 获取当前线程
- ThreadLocalMap map = getMap(t); // 根据当前线程获取ThreadLocalMap, Thread#threadLocals,子类可重写getMap()方法,比如InheritableThreadLocal, 返回的就是Thread#inheritableThreadLocals
- if (map != null) {
- ThreadLocalMap.Entry e = map.getEntry(this); // 以当前ThreadLocal对象为key, 取得value(ThreadLocalMap.Entry)
- if (e != null) {
- @SuppressWarnings("unchecked")
- T result = (T)e.value; // 返回结果
- return result;
- }
- }
- return setInitialValue(); // 如果map为空,执行初始化
- }
getMap()
- ThreadLocalMap getMap(Thread t) { // 获得map
- return t.threadLocals;
- }
setInitialValue()
- private T setInitialValue() { // 设置初始值
- T value = initialValue(); // 取得初始值
- Thread t = Thread.currentThread(); // 当前线程
- ThreadLocalMap map = getMap(t); // 获取map
- if (map != null) // 不为空,设置value, key为当前对象
- map.set(this, value);
- else
- createMap(t, value); // 否则,创建map
- return value;
- }
set()
- public void set(T value) { // 设置值,逻辑同setInitialValue()
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- map.set(this, value);
- else
- createMap(t, value);
- }
remove()
- public void remove() { // 移除
- ThreadLocalMap m = getMap(Thread.currentThread());
- if (m != null)
- m.remove(this);
- }
创建map(ThreadLocalMap)
- void createMap(Thread t, T firstValue) { // 创建map(ThreadLocalMap)
- t.threadLocals = new ThreadLocalMap(this, firstValue);
- }
- static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { // 继承map值
- return new ThreadLocalMap(parentMap);
- }
ThreadLocalMap
属性
- static class Entry extends WeakReference<ThreadLocal<?>> { // 弱引用
- Object value;
- Entry(ThreadLocal<?> k, Object v) {
- super(k);
- value = v;
- }
- }
- private static final int INITIAL_CAPACITY = 16; // 初始容量
- private Entry[] table; // 数组
- private int size = 0; // 大小
- private int threshold; // 阈值,长度的2/3
- private void setThreshold(int len) { // 设置阈值
- threshold = len * 2 / 3;
- }
- private static int nextIndex(int i, int len) { // 下一个索引
- return ((i + 1 < len) ? i + 1 : 0); // 长度范围内,加1; 超过范围,0
- }
- private static int prevIndex(int i, int len) { // 前一个索引
- return ((i - 1 >= 0) ? i - 1 : len - 1); // 长度范围内,减1; 超过范围,len - 1
- }
构造方法
- ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { // 构造方法
- table = new Entry[INITIAL_CAPACITY]; // 初始化数组,初始容量为16
- int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); // firstKey的索引
- table[i] = new Entry(firstKey, firstValue); // 构造Entry(firstKey->firstValue)对象,并置于i索引处
- size = 1; // 当前大小为1
- setThreshold(INITIAL_CAPACITY); // 设置阈值
- }
- private ThreadLocalMap(ThreadLocalMap parentMap) { // 以父ThreadLocalMap构造ThreadLocalMap
- Entry[] parentTable = parentMap.table; // 取得parentMap的table
- int len = parentTable.length; // 取得table长度
- setThreshold(len); // 设置阈值
- table = new Entry[len]; // 创建数组table
- for (int j = 0; j < len; j++) {
- Entry e = parentTable[j];
- if (e != null) {
- @SuppressWarnings("unchecked")
- ThreadLocal<Object> key = (ThreadLocal<Object>) e.get(); // 取得key
- if (key != null) {
- Object value = key.childValue(e.value); // 计算子线程的value
- Entry c = new Entry(key, value); // 构建Entry对象
- int h = key.threadLocalHashCode & (len - 1); // 计算索引
- while (table[h] != null) // 如果索引不为空,则计算下一个索引,直到找到空位
- h = nextIndex(h, len); // 寻找下一个索引,(hash碰撞时,没有使用链表,而是寻找下一个索引)
- table[h] = c;
- size++; // 长度加1
- }
- }
- }
- }
基本方法
getEntry()
- private Entry getEntry(ThreadLocal<?> key) { // 获取entry
- int i = key.threadLocalHashCode & (table.length - 1); // 计算索引
- Entry e = table[i]; // 取得entry
- if (e != null && e.get() == key) // 找到返回
- return e;
- else
- return getEntryAfterMiss(key, i, e); // 否则调用getEntryAfterMiss方法
- }
getEntryAfterMiss()
- private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { // 处理碰撞的情况
- Entry[] tab = table;
- int len = tab.length;
- while (e != null) { // 若e为空,直接返回null, 否则遍历table
- ThreadLocal<?> k = e.get(); // 获得key
- if (k == key) // 若相等,则找到返回
- return e;
- if (k == null)
- expungeStaleEntry(i); // 删除过期的Entry对象
- else
- i = nextIndex(i, len); // 计算下一个索引,继续寻找
- e = tab[i];
- }
- return null;
- }
expungeStaleEntry()
- private int expungeStaleEntry(int staleSlot) {
- Entry[] tab = table;
- int len = tab.length;
- tab[staleSlot].value = null; // value置为空
- tab[staleSlot] = null; // 槽位置为空
- size--; // size减1
- Entry e;
- int i;
- for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { // 以staleSlot起始,索引与之碰撞的所有槽位,尝试清除无效的元素
- ThreadLocal<?> k = e.get(); // key
- if (k == null) { // 过期,清理
- e.value = null;
- tab[i] = null;
- size--;
- } else {
- int h = k.threadLocalHashCode & (len - 1); // 重新归置元素
- if (h != i) {
- tab[i] = null; // 原来的槽位清空
- while (tab[h] != null) // 以h为始,找空位
- h = nextIndex(h, len);
- tab[h] = e; // 设置元素
- }
- }
- }
- return i;
- }
set()
- private void set(ThreadLocal<?> key, Object value) {
- Entry[] tab = table;
- int len = tab.length;
- int i = key.threadLocalHashCode & (len - 1); // 计算索引
- for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
- ThreadLocal<?> k = e.get();
- if (k == key) { // 命中,更新value, 返回
- e.value = value;
- return;
- }
- if (k == null) { // 替换过期的槽位
- replaceStaleEntry(key, value, i);
- return;
- }
- }
- tab[i] = new Entry(key, value); // 找到空位,新建Entry对象
- int sz = ++size; // size加1
- if (!cleanSomeSlots(i, sz) && sz >= threshold) // 清理槽位失败,并且当前size大于阈值,调用rehash方法
- rehash();
- }
replaceStaleEntry()
- private void replaceStaleEntry(ThreadLocal<?> key, Object value, int staleSlot) {
- Entry[] tab = table;
- int len = tab.length;
- Entry e;
- int slotToExpunge = staleSlot;
- for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len)) // 从当前staleSlot往前找
- if (e.get() == null)
- slotToExpunge = i; // 过期槽位起始处,接下来从slotToExpunge清理过期槽位
- for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { // 从当前staleSlot往后找
- ThreadLocal<?> k = e.get();
- if (k == key) { // 命中,替换
- e.value = value; // 替换value
- tab[i] = tab[staleSlot]; // 交换i和staleSlot的元素,i槽位处等待被清理
- tab[staleSlot] = e;
- if (slotToExpunge == staleSlot) // 在staleSlot槽位之前没有过期的槽位,将slotToExpunge设置为i(staleSlot之后的槽位,因为staleSlot已经设置了有效的元素)
- slotToExpunge = i;
- cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); // 清理工作
- return;
- }
- if (k == null && slotToExpunge == staleSlot)
- slotToExpunge = i; // 在staleSlot槽位之前没有过期的槽位,将slotToExpunge设置为i(staleSlot之后的槽位,因为staleSlot后面会设置有效的元素)
- }
- tab[staleSlot].value = null; // 置空
- tab[staleSlot] = new Entry(key, value); // 设置新的值
- if (slotToExpunge != staleSlot) // 如果有过期元素,做清理工作
- cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
- }
cleanSomeSlots()
- private boolean cleanSomeSlots(int i, int n) {
- boolean removed = false;
- Entry[] tab = table;
- int len = tab.length;
- do {
- i = nextIndex(i, len);
- Entry e = tab[i];
- if (e != null && e.get() == null) { // 找到过期元素,执行清理操作
- n = len;
- removed = true;
- i = expungeStaleEntry(i); // 具体操作还是由expungeStaleEntry完成
- }
- } while ((n >>>= 1) != 0);
- return removed;
- }
rehash()
- private void rehash() {
- expungeStaleEntries(); // 清理过期的元素
- if (size >= threshold - threshold / 4)
- resize(); // 扩容
- }
expungeStaleEntries()
- private void expungeStaleEntries() {
- Entry[] tab = table;
- int len = tab.length;
- for (int j = 0; j < len; j++) { // 从槽位0处,尝试清理过期的条目
- Entry e = tab[j];
- if (e != null && e.get() == null)
- expungeStaleEntry(j); // 调用expungeStaleEntry方法
- }
- }
resize()
- private void resize() { // 扩容
- Entry[] oldTab = table;
- int oldLen = oldTab.length;
- int newLen = oldLen * 2; // 2倍
- Entry[] newTab = new Entry[newLen]; // 新数组
- int count = 0;
- for (int j = 0; j < oldLen; ++j) {
- Entry e = oldTab[j];
- if (e != null) {
- ThreadLocal<?> k = e.get();
- if (k == null) {
- e.value = null; // 帮助GC
- } else {
- int h = k.threadLocalHashCode & (newLen - 1); // rehash
- while (newTab[h] != null) // 碰撞,计算下一个索引(槽位)
- h = nextIndex(h, newLen);
- newTab[h] = e;
- count++;
- }
- }
- }
- setThreshold(newLen); // 设置新的阈值
- size = count;
- table = newTab;
- }
remove()
- private void remove(ThreadLocal<?> key) {
- Entry[] tab = table;
- 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(); // 移除
- expungeStaleEntry(i); // 并清理过期槽位
- return;
- }
- }
- }
行文至此结束。
尊重他人的劳动,转载请注明出处:http://www.cnblogs.com/aniao/p/aniao_threadlocal.html
【Java源码解析】ThreadLocal的更多相关文章
- 【Java源码解析】Thread
简介 线程本质上也是进程.线程机制提供了在同一程序内共享内存地址空间运行的一组线程.对于内核来讲,它就是进程,只是该进程和其他一下进程共享某些资源,比如地址空间.在Java语言里,Thread类封装了 ...
- Java源码解析——集合框架(三)——Vector
Vector源码解析 首先说一下Vector和ArrayList的区别: (1) Vector的所有方法都是有synchronized关键字的,即每一个方法都是同步的,所以在使用起来效率会非常低,但是 ...
- Java源码解析——集合框架(二)——ArrayBlockingQueue
ArrayBlockingQueue源码解析 ArrayBlockingQueue是一个阻塞式的队列,继承自AbstractBlockingQueue,间接的实现了Queue接口和Collection ...
- [Java源码解析] -- String类的compareTo(String otherString)方法的源码解析
String类下的compareTo(String otherString)方法的源码解析 一. 前言 近日研究了一下String类的一些方法, 通过查看源码, 对一些常用的方法也有了更透彻的认识, ...
- [java源码解析]对HashMap源码的分析(二)
上文我们讲了HashMap那骚骚的逻辑结构,这一篇我们来吹吹它的实现思想,也就是算法层面.有兴趣看下或者回顾上一篇HashMap逻辑层面的,可以看下HashMap源码解析(一).使用了哈希表得“拉链法 ...
- java源码解析
String深入解析 String具有不变性的原因: String被final修饰,它不可能被继承,也就是任何对String的操作方法,都不会被继承覆写 String中保存数据的是一个char数组的v ...
- java源码解析——Stack类
在java中,Stack类继承了Vector类.Vector类和我们经常使用的ArrayList是类似的,底层也是使用了数组来实现,只不过Vector是线程安全的.因此可以知道Stack也是线程安全的 ...
- java源码解析之Object类
一.Object类概述 Object类是java中类层次的根,是所有类的基类.在编译时会自动导入.Object中的方法如下: 二.方法详解 Object的方法可以分成两类,一类是被关键字fin ...
- Java源码解析——集合框架(五)——HashMap源码分析
HashMap源码分析 HashMap的底层实现是面试中问到最多的,其原理也更加复杂,涉及的知识也越多,在项目中的使用也最多.因此清晰分析出其底层源码对于深刻理解其实现有重要的意义,jdk1.8之后其 ...
随机推荐
- Linux系统更改/关闭防火墙
更改/关闭防火墙 查看当前防火墙状态 /etc/init.d/iptables status ==>service iptables status 打开防火墙 service iptables ...
- CSS学习摘要-语法和选择器
主要摘自网络开发者. 从最基本的层次来看,CSS是由两块内容组合而成的: 属性(Property):一些人类可理解的标识符,这些标识符指出你想修改哪一些样式,例如:字体,宽度,背景颜色等. 属性值(V ...
- $.ajax 在请求没有完成,是可以往下继续执行js代码的
$.ajax({ url:url, data:{}, success:function(arr) { var varHtml='<option value="" checke ...
- XtraEditors一、总体介绍
一.所有编辑器的公共功能 全部都可以绑定数据: 全部都可以独立使用或用于由 Developer Express 提供的容器控件 (XtraGrid.XtraVerticalGrid.XtraTreeL ...
- Promise & Deferred objects in JavaScript Pt.1: Theory and Semantics.
原文:http://blog.mediumequalsmessage.com/promise-deferred-objects-in-javascript-pt1-theory-and-semanti ...
- PHP字符串——简单应用
错误的学习编码语言观点:语言语法不用记,多练习就可以了. 应该是死记住,然后加强练习以免忘记.以及在练习中加强理解.试着想“为什么是这样的语法习惯”.PHP提供了8种数据类型.4种是标量型(单值) ...
- CPU中MMU的作用
http://blog.csdn.net/jjw97_5/article/details/39340261 MMU是个硬件,每当cpu访问一个地址的时候,MMU从内存里面查table,把cpu想访问的 ...
- 1854. [SCOI2010]游戏【二分图】
Description lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示.当他使用某种装备时,他只能使用该装备的某一个属性 ...
- 【洛谷】【线段树+位运算】P2574 XOR的艺术
[题目描述:] AKN觉得第一题太水了,不屑于写第一题,所以他又玩起了新的游戏.在游戏中,他发现,这个游戏的伤害计算有一个规律,规律如下 1. 拥有一个伤害串为长度为n的01串. 2. 给定一个范围[ ...
- mysql 去除特殊字符和前后空白字符
mysql 去除特殊字符 update table set field = replace(replace(replace(field,char(9),''),char(10),''),char(13 ...