当前位置:网站首页>做一篇人人能搞懂的ThreadLocal(源码)
做一篇人人能搞懂的ThreadLocal(源码)
2022-06-27 13:46:00 【叫我老伯】
目录
引入:
我们知道在我们多线程中,如果同时访问同一共享变量,可能会出现线程安全问题,为了保证线程安全,我们常常会在访问这个共享变量的时候加锁,来达到同步效果。

可能会造成死锁,故引入 ThreadLocal
1、ThreadLocal
ThreadLocal是JDK提供的,支持线程本地变量。也就是说,如果我们创建了一个ThreadLocal变量,则访问这个变量的每个线程都会有这个变量的一个本地副本。如果多个线程同时对这个变量进行读写操作时,实际上操作的是线程自己本地内存中的变量,从而避免了线程安全的问题。

- ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。
二、ThreadLocal与Synchronized的区别
共同点:
- ThreadLocal和Synchonized都用于解决多线程并发访问。
区别:
- Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离
- Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
三、ThreadLocal分析
初始

改变:增加删除ThreadLocal中的变量操作

线程A和线程B存储在ThreadLocal中的变量互不干扰,线程A存储的变量只能由线程A访问,线程B存储的变量只能由线程B访问。
四、ThreadLocal源码分析
Set()方法
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//以当前线程为Key,获取ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
//获取的ThreadLocalMap对象不为空
if (map != null)
//设置value的值
map.set(this, value);
else
//获取的ThreadLocalMap对象为空,创建Thread类中的threadLocals变量
createMap(t, value);
}
//这里的this指的是代码中调用set的那个对象,也就是ThreadLocal对象从上面的代码可以看出,ThreadLocal set赋值的时候首先会获取当前线程thread,并使用当前线程作为Key调用getMap(t)方法thread线程中的ThreadLocalMap属性。如果map属性不为空,则直接更新value值,如果map为空,则程序调用createMap(t, value)方法来实例化Thread类的threadLocals成员变量。也就是创建当前线程的threadLocals变量
- getMap()
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
getMap(Thread t)方法获取的是线程变量自身的threadLocals成员变量- ThreadLocalMap ()
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}看出ThreadLocalMap是ThreadLocal的内部静态类,而它的构成主要是用Entry来保存数据 ,而且还是继承的弱引用。在Entry内部使用ThreadLocal作为key,使用我们设置的value作为value。
- createMap()
//这个是threadlocal 的内部方法
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//ThreadLocalMap 构造方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}get()方法
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程的threadLocals成员变量
ThreadLocalMap map = getMap(t);
//获取的threadLocals变量不为空
if (map != null) {
//返回本地变量对应的值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//初始化threadLocals成员变量的值
return setInitialValue();
}通过当前线程来获取threadLocals成员变量,如果threadLocals成员变量不为空,则直接返回当前线程绑定的本地变量,否则调用setInitialValue()方法初始化threadLocals成员变量的值。
- etInitialValue()
private T setInitialValue() {
//调用初始化Value的方法
T value = initialValue();
Thread t = Thread.currentThread();
//根据当前线程获取threadLocals成员变量
ThreadLocalMap map = getMap(t);
if (map != null)
//threadLocals不为空,则设置value值
map.set(this, value);
else
//threadLocals为空,创建threadLocals变量
createMap(t, value);
return value;
}- initialValue()
protected T initialValue() {
return null;
}remove()方法
public void remove() {
//根据当前线程获取threadLocals成员变量
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
//threadLocals成员变量不为空,则移除value值
m.remove(this);
}remove方法,直接将ThrealLocal 对应的值从当前相差Thread中的ThreadLocalMap中删除。为什么要删除,这涉及到内存泄露的问题。
实际上 ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,弱引用的特点是,如果这个对象只存在弱引用,那么在下一次垃圾回收的时候必然会被清理掉。
所以如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候会被清理掉的,这样一来 ThreadLocalMap中使用这个 ThreadLocal 的 key 也会被清理掉。但是,value 是强引用,不会被清理,这样一来就会出现 key 为 null 的 value。
五、ThreadLocal的数据结构

- 弱引用
ThreadLocalMap有自己的独立实现,可以简单地将它的key视作ThreadLocal,value为代码中放入的值(实际上key并不是ThreadLocal本身,而是它的一个弱引用)
- 线程隔离
- 每个线程在往
ThreadLocal里放值的时候,都会往自己的ThreadLocalMap里存,读也是以ThreadLocal作为引用,在自己的map里找对应的key,从而实现了线程隔离。
- 每个线程在往
- 结构
ThreadLocalMap有点类似HashMap的结构,只是HashMap是由数组+链表实现的,而ThreadLocalMap中并没有链表结构。
补:Java的四种引用类型
强引用:我们常常new出来的对象就是强引用类型,只要强引用存在,垃圾回收器将永远不会回收被引用的对象,哪怕内存不足的时候
软引用:使用SoftReference修饰的对象被称为软引用,软引用指向的对象在内存要溢出的时候被回收
弱引用:使用WeakReference修饰的对象被称为弱引用,只要发生垃圾回收,若这个对象只被弱引用指向,那么就会被回收
虚引用:虚引用是最弱的引用,在 Java 中使用 PhantomReference 进行定义。虚引用中唯一的作用就是用队列接收对象即将死亡的通知
边栏推荐
猜你喜欢

【业务安全03】密码找回业务安全以及接口参数账号修改实例(基于metinfov4.0平台)

赛迪顾问发布《“十四五” 关键应用领域之数据库市场研究报告》(附下载)

【OS命令注入】常见OS命令执行函数以及OS命令注入利用实例以及靶场实验—基于DVWA靶场

基于 xml 配置文件的入门级 SSM 框架整合

Learning records of numpy Library

Step by step expansion of variable parameters in class templates

Kyndryl与Oracle和Veritas达成合作

Explore tidb lightning source code to solve the found bugs

Does Xinhua San still have to rely on ICT to realize its 100 billion enterprise dream?

Pytorch learning 3 (test training model)
随机推荐
剑指 Offer II 039. 直方图最大矩形面积 单调栈
Journal quotidien des questions (6)
微服务如何拆分
Crane: a new way of dealing with dictionary items and associated data
ensp云朵配置
[OS command injection] common OS command execution functions and OS command injection utilization examples and range experiments - based on DVWA range
芯片供给过剩之际,进口最多的中国继续减少进口,美国芯片慌了
全球芯片市场或陷入停滞,中国芯片逆势扩张加速提升自给率
EventLoop learning
Kyndryl与Oracle和Veritas达成合作
Pytorch learning 1 (learning documents on the official website)
【PHP代码注入】PHP语言常见可注入函数以及PHP代码注入漏洞的利用实例
POSIX AIO -- Introduction to glibc version asynchronous IO
How to split microservices
Type 'image' is not a subtype of type 'imageprovider < object > solution
防火墙基础之华为华三防火墙web页面登录
Debug tool
Does Xinhua San still have to rely on ICT to realize its 100 billion enterprise dream?
MySQL 索引及其分类
How ASP connects Excel