ThreadLocal 原理分析
用法
ThreadLocal<String> threadLocal = new ThreadLocal<>(); // 无初始值
ThreadLocal<String> threadLocal = ThreadLocal.withInitial(() -> "123"); // 有初始值
threadLocal.set("123"); // set操作
threadLocal.get(); // get操作
threadLocal.remove(); // remove操作
一个小例子:
public static void main(String[] args) throws InterruptedException {
ThreadLocal threadLocal = new InheritableThreadLocal();
threadLocal.set("Hello");
System.out.println("现在线程是" + Thread.currentThread().getName() + ", 尝试获取:" + threadLocal.get());
new Thread(() -> {
threadLocal.set("World");
System.out.println("现在线程是" + Thread.currentThread().getName() + ", 尝试获取:" + threadLocal.get());
threadLocal.remove();
}).start();
Thread.sleep(3000);
System.out.println("现在线程是" + Thread.currentThread().getName() + ", 尝试获取:" + threadLocal.get());
threadLocal.remove();
}
输出:
现在线程是main, 尝试获取:Hello
现在线程是Thread-0, 尝试获取:World
现在线程是main, 尝试获取:Hello
实现
set操作
public void set(T value) {
Thread t = Thread.currentThread(); // 获取当前线程
ThreadLocalMap map = getMap(t); // 获取ThreadLocalMap
if (map != null)
map.set(this, value);
else
createMap(t, value); // 创建map
}
// getMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
}
// createMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY]; // INITIAL_CAPACITY = 16;
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); // 计算下标
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY); // 设置阈值
}
// 哈希值
private final int threadLocalHashCode = nextHashCode();
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT); // HASH_INCREMENT = 0x61c88647;
}
// ThreadLocalMap数据结构
static class ThreadLocalMap {
private Entry[] table;
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}

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(); // 获取弱引用ThreadLocal
if (k == key) { // 对于已经存在的key,直接赋值
e.value = value;
return;
}
if (k == null) { // 弱引用ThreadLocal为null,进行替换
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value); // 上面两种情况不是,直接赋值
int sz = ++size; // size+1
if (!cleanSomeSlots(i, sz) && sz >= threshold) // 清除无效entry,大于阈值,扩容并重新散列化
rehash();
}
get操作
public T get() {
Thread t = Thread.currentThread(); // 获取当前线程
ThreadLocalMap map = getMap(t); // 获取ThreadLocalMap
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); // 传入自己,也就是threadlocal对象,得到entry
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1); // 获得下标
Entry e = table[i]; // 取值
if (e != null && e.get() == key) // 正好取到,直接返回
return e;
else
return getEntryAfterMiss(key, i, e);
}
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) { // 依次加一往后取值
ThreadLocal<?> k = e.get();
if (k == key) // 取到返回
return e;
if (k == null) // 弱引用ThreadLocal为null,清除无效的entry
expungeStaleEntry(i); //
else
i = nextIndex(i, len); // 下一个
e = tab[i];
}
return null;
}
remove操作
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
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(); // 弱引用的引用置为null
expungeStaleEntry(i); // 清除entry,并重新散列化
return;
}
}
}
public void clear() {
this.referent = null;
}
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
// 槽置为null
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--;
// 后面的进行重新散列化
Entry e;
int i;
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();
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 = nextIndex(h, len);
tab[h] = e;
}
}
}
return i;
}
内存泄露

当ThreadLocal没有强依赖,ThreadLocal会在下一次发生GC时被回收,key是被回收了,但是value却没有被回收,为了防止这个问题出现,最好手动调用remove方法。
ThreadLocal 原理分析的更多相关文章
- java基础解析系列(七)---ThreadLocal原理分析
java基础解析系列(七)---ThreadLocal原理分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)-- ...
- ThreadLocal原理分析
本文结构 ThreadLocal简介 (简要说明ThreadLocal的作用) ThreadLocal实现原理(说明ThreadLocal的常用方法和原理) ThreadLocalMap的实现 (说明 ...
- ThreadLocal原理分析与使用场景
什么是ThreadLocal变量 ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本.这里有几点需要注意: 因为每个 Thr ...
- ThreadLocal原理分析与代码验证
ThreadLocal提供了线程安全的数据存储和访问方式,利用不带key的get和set方法,居然能做到线程之间隔离,非常神奇. 比如 ThreadLocal<String> thread ...
- ThreadLocal原理及其实际应用
前言 java猿在面试中,经常会被问到1个问题: java实现同步有哪几种方式? 大家一般都会回答使用synchronized, 那么还有其他方式吗? 答案是肯定的, 另外一种方式也就是本文要说的Th ...
- Handler系列之原理分析
上一节我们讲解了Handler的基本使用方法,也是平时大家用到的最多的使用方式.那么本节让我们来学习一下Handler的工作原理吧!!! 我们知道Android中我们只能在ui线程(主线程)更新ui信 ...
- [转]Handler MessageQueue Looper消息循环原理分析
Handler MessageQueue Looper消息循环原理分析 Handler概述 Handler在Android开发中非常重要,最常见的使用场景就是在子线程需要更新UI,用Handler ...
- Junit 注解 类加载器 .动态代理 jdbc 连接池 DButils 事务 Arraylist Linklist hashset 异常 哈希表的数据结构,存储过程 Map Object String Stringbufere File类 文件过滤器_原理分析 flush方法和close方法 序列号冲突问题
Junit 注解 3).其它注意事项: 1).@Test运行的方法,不能有形参: 2).@Test运行的方法,不能有返回值: 3).@Test运行的方法,不能是静态方法: 4).在一个类中,可以同时定 ...
- Eventbus 使用方法和原理分析
对于 Eventbus ,相信很多 Android 小伙伴都用到过. 1.创建事件实体类 所谓的事件实体类,就是传递的事件,一个组件向另一个组件发送的信息可以储存在一个类中,该类就是一个事件,会被 E ...
随机推荐
- spark踩坑--WARN ProcfsMetricsGetter: Exception when trying to compute pagesize的最全解法
spark踩坑--WARN ProcfsMetricsGetter: Exception when trying to compute pagesize的最全解法 问题描述 大概是今年上半年的时候装了 ...
- Open_basedir绕过
Open_basedir绕过 一.基础知识 open_basedir是php.ini中的一个配置选项,它可将用户访问文件的活动范围限制在指定的区域, 假设open_basedir=/home/wwwr ...
- Python 带你高效创作短视频,视频创作秀到飞起!!!
近两年,抖音.快手将短视频推到风口浪尖上,要生产出高质量的视频,离不开视频剪辑这一环节:在全民剪片浪潮中,大众使用最多的剪辑软件如:Pr.FCPX.剪印.Vue 等. 视频剪辑过程中,Python 一 ...
- matlab随机系数矩阵产生以及矩阵的可视化函数
clc; clear all; close all; n = 100;%所产生矩阵的大小 A= sprandsym(n,0.015,0.1,1);%产生系数矩阵函数: spy(A)矩阵图形化相当于im ...
- 容器编排系统K8s之Volume的基础使用
前文我们聊到了k8s上的ingress资源相关话题,回顾请参考:https://www.cnblogs.com/qiuhom-1874/p/14167581.html:今天们来聊一下k8s上volum ...
- C# HTML帮助类 包括补全标签 截取HTML字符串包含标签
public static class HtmlHelper { /// <summary> /// 按文本内容长度截取HTML字符串(支持截取带HTML代码样式的字符串) /// < ...
- 使用h5开发跨平台APP确保数据安全交互---服务器篇
从eclipse到android studio的安卓开发经验告诉我原声开发才是硬道理,其实以前很抵触html5开发app的,虽然没有去了解过,但是冥冥中就觉得它运行速度太慢了,加载渲染根本比不上原生开 ...
- Windows 安装 Go语言开发环境
Windows 安装 Go语言开发环境 下载安装包 下载地址:http://www.golangtc.com/download 32 位请选择名称中包含 windows-386 的 msi 安 ...
- 【mybatis-plus】什么是乐观锁?如何实现“乐观锁”
"乐观锁"这个词以前我也没听过.上次在测试需求的时候,查询数据库发现有一个version字段,于是请教开发这个字干嘛使, 人家回复我:乐观锁,解决并发更新用的.当时大家都忙,咱也不 ...
- .NET 5 开源工作流框架elsa技术研究
今天假期第一天,研究了.NET 5开源工作流框架elsa,现在分享给大家. 一.框架简介 elsa是一个开源的.NET Standard 工作流框架,官方网站:https://elsa-workflo ...