ThreadLocal是什么?谈谈你对他的理解
1、ThreadLocal是什么
从名字我们就可以看到ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
2、ThreadLocal怎么用
public class TestTreadLocal {
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
threadLocal.set("kkk");
String s = threadLocal.get();
System.out.println(s);
threadLocal.remove();
}
});
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
threadLocal.set("iiiii");
String s = threadLocal.get();
System.out.println(s);
threadLocal.remove();
}
});
thread.start();
thread1.start();
}
}
上述可以看到,不同的线程对共享的变量操作都互不影响
3、ThreadLocal源码分析
接下来我们看看threadlocal源码
set()的源码
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
jiexialo从set方法我们可以看到,首先获取到了当前线程t,然后调用getMap获取ThreadLocalMap,如果map存在,则将当前线程对象t作为key,要存储的对象作为value存到map里面去。如果该Map不存在,则初始化一个。
接下来,看一下threadlocal源码
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;
}
}
//省略
可以看到ThreadloacalMap就是threadloacal的静态内部类,里面定义了一个entry保存数据,以threadloacal为key,我们设置的值value作为value
getmap()源码
//ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
上述代码其实就是调用threadlocalmap
get()源码
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
上述为get()源码,可以看出首先获取当前线程threadlocalmap,如果map不为null,则获取当前线程的entry。entry的值value就作为value返回
那如果map为null则初始化一个值
private T setInitialValue() {
T value = initialValue();//初始化一个值
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);//创建一个map
return value;
}
protected T initialValue() {
return null;//null
}
remove()源码
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
从源码可以看出,将当前线程作为可以移除。源码跟下去看看remove方法
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();
expungeStaleEntry(i);
return;
}
}
}
可以看出,remove会移除该线程的所有value
总结:
(1)每个Thread维护着一个ThreadLocalMap的引用
(2)ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储
(3)ThreadLocal创建的副本是存储在自己的threadLocals中的,也就是自己的ThreadLocalMap。
(4)ThreadLocalMap的键值为ThreadLocal对象,而且可以有多个threadLocal变量,因此保存在map中
(5)在进行get之前,必须先set,否则会报空指针异常,当然也可以初始化一个,但是必须重写initialValue()方法。
(6)ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。
OK,现在从源码的角度上不知道你能理解不,对于ThreadLocal来说关键就是内部的ThreadLocalMap。
4、ThreadLocal内存泄漏问题
上面这张图详细的揭示了ThreadLocal和Thread以及ThreadLocalMap三者的关系。
1、Thread中有一个map,就是ThreadLocalMap
2、ThreadLocalMap的key是ThreadLocal,值是我们自己设定的。
3、ThreadLocal是一个弱引用,当为null时,会被当成垃圾回收
4、重点来了,突然我们ThreadLocal是null了,也就是要被垃圾回收器回收了,但是此时我们的ThreadLocalMap生命周期和Thread的一样,它不会回收,这时候就出现了一个现象。那就是ThreadLocalMap的key没了,但是value还在,这就造成了内存泄漏。
解决办法:使用完ThreadLocal后,执行remove操作,避免出现内存溢出情况。
ThreadLocal是什么?谈谈你对他的理解的更多相关文章
- 面试官:知道ThreadLocal嘛?谈谈你对它的理解?(基于jdk1.8)
https://zhuanlan.zhihu.com/p/99150038 西北工业大学 计算机技术硕士在读 在java的多线程模块中,ThreadLocal是经常被提问到的一个知识点,提问的 ...
- 谈谈嵌套for循环的理解
谈谈嵌套for循环的理解 说for的嵌套,先说一下一个for循环的是怎么用的. 这次的目的是为了用for循环输出一个乘法口诀表,一下就是我的一步步理解. 一. 语法: ...
- JVM(一),谈谈你对java的理解
一.谈谈你对java的理解 1.Java特性 (1)平台无关性 一次编译到处运行 (2)GC 垃圾回收机制 (3)语言特性 泛型-反射机制-lambda表达式 (4)面向对象 面向对象语言-三大特性( ...
- 【面试普通人VS高手系列】谈谈你对AQS的理解
AQS是AbstractQueuedSynchronizer的简称,是并发编程中比较核心的组件. 在很多大厂的面试中,面试官对于并发编程的考核要求相对较高,简单来说,如果你不懂并发编程,那么你很难通过 ...
- 【Java面试】面试遇到宽泛的问题,这么回答就稳了,谈谈你对Redis的理解
"谈谈你对Redis的理解"! 面试的时候遇到这类比较宽泛的问题,是不是很抓狂? 是不是不知道从何开始说起? 没关系,今天我用3分钟教你怎么回答. 大家好,我是Mic,一个工作了1 ...
- 谈谈对Spring IOC的理解(转)
学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...
- 谈谈对Spring IOC的理解
学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...
- Spring系列之谈谈对Spring IOC的理解
学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IOC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...
- 谈谈对Spring IOC的理解【转】
学习过Spring框架的人 一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大 ...
随机推荐
- 002_centos7关闭防火墙
防火墙是比较烦人的,在自己做实验,或者实际应用中,如果配置不好的话,会出现各种匪夷所思的问题,那么如何关闭呢 在centos7里,防火墙改为了firewalld进程 首先用命令firewall-cmd ...
- linux tcpdump抓包Post请求
tcpdump -s 0 -A 'tcp dst port 80 and (tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354)' -w f ...
- SpringCloud微服务:基于Nacos组件,整合Dubbo框架
源码地址:GitHub·点这里 || GitEE·点这里 一.基础组件简介 1.Dubbo框架 Dubbo服务化治理的核心框架,之前几年在国内被广泛使用,后续由于微服务的架构的崛起,更多的公司转向微服 ...
- 社区观点 | 理解比原链MOV链上交换协议
去中心化交换协议的发展 从Bitshare,Stellar到以太坊上的Etherdelta,Bancor,0x协议,去中心化交换协议也经过了好几代发展和很多模式的探索,每一代都通过前面的协议的痛点来进 ...
- object detection 总结
1.基础 自己对于YOLOV1,2,3都比较熟悉. RCNN也比较熟悉.这个是自己目前掌握的基础2.第一步 看一下2019年的井喷的anchor free的网络3.第二步 看一下以往,引用多的网路4. ...
- Vulnhub靶场-Me Tomcat Host 学习笔记
Nmap -sS -Pn 192.168.232.0/24 nmap -v -A -sS -Pn -T4 -p 1-65535 192.168.232.132 扫页面 数据库部署成功后打开msf,利用 ...
- C# BackGroundWorker实现窗体不卡死 进度条功能
网上的例子少了好多属性的配置,改好了一个能直接用的: using System; using System.Collections.Generic; using System.ComponentMod ...
- 刚体验完RabbitMQ?一文带你SpringBoot+RabbitMQ方式收发消息
人生终将是场单人旅途,孤独之前是迷茫,孤独过后是成长. 楔子 这篇是消息队列RabbitMQ的第二弹. 上一篇的结尾我也预告了本篇的内容:利用RabbitTemplate和注解进行收发消息,还有一个我 ...
- C#LeetCode刷题之#27-移除元素(Remove Element)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3624 访问. 给定一个数组 nums 和一个值 val,你需要原 ...
- Vue element-ui el-table阻止行选事件
我们经常会在某个table末尾加上操作列来放置button来处理跳转和其他的逻辑 那么当点击button的时候同样也会执行在el-table 设置的 @row-click="handleRo ...