用法

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. 免杀shellcode并绕过杀毒添加自启动

    https://www.wtfsec.org/posts/%E5%85%8D%E6%9D%80shellcode%E5%B9%B6%E7%BB%95%E8%BF%87%E6%9D%80%E6%AF%9 ...

  2. JDK8新特性详解(二)

    Stream流的使用 流操作是Java8提供一个重要新特性,它允许开发人员以声明性方式处理集合,其核心类库主要改进了对集合类的 API和新增Stream操作.Stream类中每一个方法都对应集合上的一 ...

  3. android studio 找不到真机设备

    连接USB之后没有显示连接,如下图 设备管理器: 解决:重启电脑

  4. 个人MySQL的事务特性原理学习笔记总结

    目录 个人MySQL的事务特性原理笔记总结 一.基础概念 2. 事务控制语句 3. 事务特性 二.原子性 1. 原子性定义 2. 实现 三.持久性 1. 定义 2. 实现 3. redo log存在的 ...

  5. SSRF之利用dict和gopher吊打Redis

    SSRF之利用dict和gopher吊打Redis 写在前面 SSRF打Redis也是老生常谈的东西了,这里复现学习一下之前在xz看到某师傅写的关于SSRF利用dict和gopher打内网服务的文章, ...

  6. TurtleBot3 Waffle (tx2版华夫)(3)opencr系统安装

    3. 1. 安装 Arduino IDE Opencr的安装环境的安装包,双击打开即可.进入安装的过程. 在这里你可以选择自己的安装位置. 安装已经完成,点击CLOSE 即可. 3.2. opencr ...

  7. .Net 5中Windows Forms运行时的新功能(翻译)

    本文翻译自Igor的文章,原文地址:https://devblogs.microsoft.com/dotnet/whats-new-in-windows-forms-runtime-in-net-5- ...

  8. 雅虎(ycsb)测试hbase(压测)

    一.下载ycsb 0.10包 https://github.com/brianfrankcooper/YCSB/releases/download/0.10.0/ycsb-0.10.0.tar.gz ...

  9. 20210105 - python自动化办公简介

    新的一年开始了, 计划每周至少更新三篇博客. 人生苦短,如果不做改变,人生很快会过去!2021年寻求改变,加油! python自动化办公: 1.相关工具与环境的安装概要: 需要用到python(一种开 ...

  10. spring cache 学习——@CachePut 使用详解

    1. 功能说明 当需要在不影响方法执行的情况下更新缓存时,可以使用 @CachePut,也就是说,被 @CachePut 注解的缓存方法总是会执行,而且会尝试将结果放入缓存(当然,是否真的会缓存还跟一 ...