- 📖 ThreadLocal 详解
- 📑 ThreadLocal 简介
- 📑 ThreadLocal 与 Synchronized 区别
- 📑 ThreadLocal 的基本使用
- 📑 ThreadLocal 的原理
- 🔖 ThreadLocal 的 set() 方法
- 🔖 什么是ThreadLocalMap
- 🔖 ThreadLocal 的 get() 方法
- 🔖 ThreadLocal 的 remove()方法
- 📚 内存泄漏问题
- 🔖 Thread 、 ThreadLocalMap 与 ThreadLocal 三者关系
- 📑 ThreadLocal 实用场景
- 🔖 基于ZooKeeper 与 ThreadLocal 实现的分布式锁
📖 ThreadLocal 详解 📑 ThreadLocal 简介本文是参考了 史上最全ThreadLocal 详解 的绝大部分知识点,以及针对其细致部分源码进行理解的笔记,如有侵权请通知俺 俺会删除的~
什么是ThreadLocal ?
ThreadLocal:顾名思义——线程变量
,也就是说 ThreadLocal 中的填充变量
是属于当前线程的(Local) ,变量是当前线程所独占的。 ThreadLocal 为变量在每一个线程当中都创建好了副本
,每个线程都可以访问自己内部的副本变量。
📚注意点:
同一个ThreadLocal所包含的对象,在不同的Thread中有不同的副本
- 每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来
- 每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题。
ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用
,也即变量在线程间隔离而在方法或类间共享的场景
ThreadLocal
是与线程绑定的一个变量
共同点
ThreadLocal 与 Synchronized 都用于解决多线程并发访问
本质区别
- Synchronized 是用于线程间的数据共享 , ThreadLocal 是用于线程间的数据隔离
- Synchronized 是利用锁的机制,使得变量或者代码在某一个时刻只能被一个线程访问,而主要用于多个线程之间通信能够获得数据共享
- ThreadLocal 为每一个线程都提供了变量副本,从而使得在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。
💥 本质理解
ThreadLocal里面存东西就是向它里面的Map存东西的,然后ThreadLocal把这个Map挂到当前的线程底下,这样Map就只属于这个线程了。
📑 ThreadLocal 的基本使用ThreadLocalDemo.java
/***
* @author: Alascanfu
* @date : Created in 2022/4/14 17:19
* @description: ThreadLocalDemo
* @modified By: Alascanfu
**/
public class ThreadLocalDemo {
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
static void print(String str){
// Prints the value of a local variable in local memory in the current thread
System.out.println(str + " : "+threadLocal.get());
// Clears variables in local memory
threadLocal.remove();
}
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
ThreadLocalDemo.threadLocal.set("local_A");
print("A");
System.out.println("after remove :" + threadLocal.get());
},"A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
ThreadLocalDemo.threadLocal.set("local_B");
print("B");
System.out.println("after remove :" + threadLocal.get());
},"B").start();
}
}
执行结果
A : local_A
after remove :null
B : local_B
after remove :null
📚 总结:
上述程序分别获取了自己线程存放的变量,他们之间变量的获取并不会错乱,这就是数据隔离性的体现。
📑 ThreadLocal 的原理 🔖 ThreadLocal 的 set() 方法源码分析
- 首先我们通过
Thread.currentThread()
获取当前线程t - 然后我们通过调用
getMap()
方法获取了线程t中的属性ThreadLocalMap
- 如果threadLocalMap 不为空 ,则直接更新要保存的变量值
- 反之创建 threadLocalMap 对象并赋值
源码分析
-
ThreadLocalMap
是ThreadLocal
中的一个静态内部类 -
Entry
类作为ThreadLocalMap
的保存数据类,同时是继承了WeakReference 弱引用
。 -
在Entry内部使用
ThreadLocal作为key
,使用我们设置的value作为value
。
createMap()方法
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap构造方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
// 初始化 Entry 数组大小 为 INITIAL_CAPACITY = 16
table = new Entry[INITIAL_CAPACITY];
// 计算哈希进行数组下标映射 将 firstKey 通过 threadLocalHashCode 计算哈希进行映射下标值 i
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
// 将对应的 Entry 对象赋值到指定数组下标
table[i] = new Entry(firstKey, firstValue);
// 设置大小
size = 1;
// 设置临界值 16 * 2 / 3
setThreshold(INITIAL_CAPACITY);
}
🔖 ThreadLocal 的 get() 方法
源码分析
- 首先获取得到
当前的线程t
,然后通过当前线程t
获取threadLocalMap- 如果
threadLocalMap
不为空时,通过threadLocalMap
去获取当前的Entry 数据结构
,这个Entry
是通过计算ThreadLocal
的映射下标从table[]
中获取得到的。- 如果
Entry
对象不为空 就将结果 value 直接返回
- 如果
- 如果
- 反之上述会通过
setInitialValue()
方法自行设置初始值 null
源码分析
- 获取得到
ThreadLocalMap
,将当前的ThreadLocal
对应的值 从ThreadLocalMap
中删除
为什么要删除?
-
实际上 ThreadLocalMap 中使用的
key
为ThreadLocal 的弱引用
, 弱引用的特点是:如果这个对象只存在弱引用
,那么在下一次垃圾回收的时候必然会被清理掉
。 -
所以 如果
ThreadLocal 没有被外部强引用的话
,垃圾回收时就会被清理掉,这样一来ThreadLocalMap 中的 key(弱引用的ThreadLocal) 也会被清理掉
,但是value 是强引用
,不会被清理掉,这样一来就出现了key 为 null 的 value -
如果
ThreadLocal中直接或间接包装了集合类或复杂对象
,每次在同一个ThreadLocal中取出对象后,再对内容做 *** 作,那么内部的集合类和复杂对象所占用的空间可能会开始持续膨胀。
简要理解
ThreadLocalMap
实际上是 Thread
的一个属性值,而 ThreadLocal
是维护 ThreadLocalMap这个属性值的一个工具类
。Thread
线程可以拥有多个 ThreadLocal 维护自己线程独享的共享变量
(这个共享变量只是针对于自己线程里面共享的)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)