用法

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

  1. java基础解析系列(七)---ThreadLocal原理分析

    java基础解析系列(七)---ThreadLocal原理分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)-- ...

  2. ThreadLocal原理分析

    本文结构 ThreadLocal简介 (简要说明ThreadLocal的作用) ThreadLocal实现原理(说明ThreadLocal的常用方法和原理) ThreadLocalMap的实现 (说明 ...

  3. ThreadLocal原理分析与使用场景

    什么是ThreadLocal变量 ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本.这里有几点需要注意: 因为每个 Thr ...

  4. ThreadLocal原理分析与代码验证

    ThreadLocal提供了线程安全的数据存储和访问方式,利用不带key的get和set方法,居然能做到线程之间隔离,非常神奇. 比如 ThreadLocal<String> thread ...

  5. ThreadLocal原理及其实际应用

    前言 java猿在面试中,经常会被问到1个问题: java实现同步有哪几种方式? 大家一般都会回答使用synchronized, 那么还有其他方式吗? 答案是肯定的, 另外一种方式也就是本文要说的Th ...

  6. Handler系列之原理分析

    上一节我们讲解了Handler的基本使用方法,也是平时大家用到的最多的使用方式.那么本节让我们来学习一下Handler的工作原理吧!!! 我们知道Android中我们只能在ui线程(主线程)更新ui信 ...

  7. [转]Handler MessageQueue Looper消息循环原理分析

    Handler MessageQueue Looper消息循环原理分析   Handler概述 Handler在Android开发中非常重要,最常见的使用场景就是在子线程需要更新UI,用Handler ...

  8. Junit 注解 类加载器 .动态代理 jdbc 连接池 DButils 事务 Arraylist Linklist hashset 异常 哈希表的数据结构,存储过程 Map Object String Stringbufere File类 文件过滤器_原理分析 flush方法和close方法 序列号冲突问题

    Junit 注解 3).其它注意事项: 1).@Test运行的方法,不能有形参: 2).@Test运行的方法,不能有返回值: 3).@Test运行的方法,不能是静态方法: 4).在一个类中,可以同时定 ...

  9. Eventbus 使用方法和原理分析

    对于 Eventbus ,相信很多 Android 小伙伴都用到过. 1.创建事件实体类 所谓的事件实体类,就是传递的事件,一个组件向另一个组件发送的信息可以储存在一个类中,该类就是一个事件,会被 E ...

随机推荐

  1. 这个 bug 让我更加理解 Spring 单例了

    我是风筝,公众号「古时的风筝」,一个兼具深度与广度的程序员鼓励师,一个本打算写诗却写起了代码的田园码农! 文章会收录在 JavaNewBee 中,更有 Java 后端知识图谱,从小白到大牛要走的路都在 ...

  2. Tomcat如何使用线程池处理远程并发请求

    Tomcat如何使用线程池处理远程并发请求 通过了解学习tomcat如何处理并发请求,了解到线程池,锁,队列,unsafe类,下面的主要代码来自 java-jre: sun.misc.Unsafe j ...

  3. (七)、touch--创建文件或者更新时间戳

    一.命令说明与格式 创建文件并更新时间戳,若要创建的文件名已经存在,则仅仅更新时间戳,而不改变其他任何信息 格式:touch     [选项]   目录名/文件名 选项: -a            ...

  4. 华为全栈AI技术干货深度解析,解锁企业AI开发“秘籍”

    摘要:针对企业AI开发应用中面临的痛点和难点,为大家带来从实践出发帮助企业构建成熟高效的AI开发流程解决方案. 在数字化转型浪潮席卷全球的今天,AI技术已经成为行业公认的升级重点,正在越来越多的领域为 ...

  5. 想成为Git大神?从学会reset开始吧

    大家好,今天我们来着重介绍一个非常关键的功能就是reset.在上一篇文章介绍修改历史记录的时候曾经提到过,当我们需要拆分一个历史提交记录的时候需要使用reset.估计很多小伙伴不明白,reset究竟做 ...

  6. 发送微信通知 java

    //实现类@Service public class WeChatServiceImpl implements IWeChatService { @Override public WeChatSend ...

  7. Net/NetCore/.NET5 ORM 六大查询体系 - SqlSugar 高级篇

    框架介绍 SqlSugar ORM是一款老牌国产ORM框架,生命力也比较顽强,从早期ORM不成熟阶段,一直存活到现在,我为什么要一直坚持,那是因为还有很多用户在使用,本来我能够较早推出新开源框架 ,可 ...

  8. ConcurrentHashMap 并发之美

    一.前言 她如暴风雨中的一叶扁舟,在高并发的大风大浪下疾驰而过,眼看就要被湮灭,却又在绝境中绝处逢生 编写一套即稳定.高效.且支持并发的代码,不说难如登天,却也绝非易事. 一直有小伙伴向我咨询关于Co ...

  9. Kafka知识总结及面试题

    目录 概念 Kafka基础概念 命令行 Kafka 数据存储设计 kafka在zookeeper中存储结构 生产者 生产者设计 消费者 消费者设计 面试题 kafka设计 请说明什么是Apache K ...

  10. linux系统重启后提示An error occurred during the file system check.

    一.问题描述 生产环境中一台浪潮NF8480M3外观红灯报警,鉴于无法登陆带外管理口,只能对服务器进行断电重启操作 二.问题现象 重启后进入开机过程并报错,正常来说进入此界面后直接输入root密码即可 ...