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是什么?谈谈你对他的理解的更多相关文章

  1. 面试官:知道ThreadLocal嘛?谈谈你对它的理解?(基于jdk1.8)

    https://zhuanlan.zhihu.com/p/99150038   ​ 西北工业大学 计算机技术硕士在读 在java的多线程模块中,ThreadLocal是经常被提问到的一个知识点,提问的 ...

  2. 谈谈嵌套for循环的理解

    谈谈嵌套for循环的理解     说for的嵌套,先说一下一个for循环的是怎么用的.      这次的目的是为了用for循环输出一个乘法口诀表,一下就是我的一步步理解.    一.   语法:   ...

  3. JVM(一),谈谈你对java的理解

    一.谈谈你对java的理解 1.Java特性 (1)平台无关性 一次编译到处运行 (2)GC 垃圾回收机制 (3)语言特性 泛型-反射机制-lambda表达式 (4)面向对象 面向对象语言-三大特性( ...

  4. 【面试普通人VS高手系列】谈谈你对AQS的理解

    AQS是AbstractQueuedSynchronizer的简称,是并发编程中比较核心的组件. 在很多大厂的面试中,面试官对于并发编程的考核要求相对较高,简单来说,如果你不懂并发编程,那么你很难通过 ...

  5. 【Java面试】面试遇到宽泛的问题,这么回答就稳了,谈谈你对Redis的理解

    "谈谈你对Redis的理解"! 面试的时候遇到这类比较宽泛的问题,是不是很抓狂? 是不是不知道从何开始说起? 没关系,今天我用3分钟教你怎么回答. 大家好,我是Mic,一个工作了1 ...

  6. 谈谈对Spring IOC的理解(转)

    学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...

  7. 谈谈对Spring IOC的理解

    学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...

  8. Spring系列之谈谈对Spring IOC的理解

    学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IOC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...

  9. 谈谈对Spring IOC的理解【转】

    学习过Spring框架的人 一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大 ...

随机推荐

  1. PHP7 生产环境队列 Beanstalkd 正确使用姿势

    应用场景 为什么要用呢,有什么好处?这应该放在最开头说,一件东西你只有了解它是干什么的,适合干什么,才能更好的与自己的项目相结合,用到哪里学到哪里,学了不用等于不会,我们平时就应该多考虑一些这样的问题 ...

  2. JavaFX桌面应用-为什么应用老是“未响应”

    日常使用软件的过程中,偶尔会遇到软件突然卡住,再点击几次就变成"未响应"的情况. 在JavaFX应用中同样也会出现这种情况,在开发过程中应该尽量避免这种情况的出现. >> ...

  3. C#LeetCode刷题之#633-平方数之和( Sum of Square Numbers)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3885 访问. 给定一个非负整数 c ,你要判断是否存在两个整数 ...

  4. C#算法设计排序篇之01-冒泡排序(附带动画演示程序)

    冒泡排序(Bubble Sort) 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/672 访问. 它重复地访问要排序的元 ...

  5. 虚拟机安装mysql遇到的坑

    相信很多同学也跟我一样在家里,windows安装个虚拟机,再安装linux.然后在linux下模拟出一套运行环境.这次我装的是mysql 8.0.中间遇到很多坑,记录一下.(用的linux为redha ...

  6. Sublime Text 3关闭更新新版本的提醒

    1 关闭更新提示前首先需要 输入激活码,下边是我的,亲测有效,无效的话,自行百度填写. ----- BEGIN LICENSE ----- ZYNGA INC. 50 User License EA7 ...

  7. 数值分析案例:Newton插值预测2019城市(Asian)温度、Crout求解城市等温性的因素系数

    数值分析案例:Newton插值预测2019城市(Asian)温度.Crout求解城市等温性的因素系数 文章目录 数值分析案例:Newton插值预测2019城市(Asian)温度.Crout求解城市等温 ...

  8. JAVA——JVM参数设置规则以及参数含义

    JVM参数设置规则: -XX:+<option> 启用option,例如:-XX:+PrintGCDetails启动打印GC信息的选项,其中+号表示true,开启的意思-XX:-<o ...

  9. Pytools1.0.0发布啦!

    大家赶紧来看看,特别好用的. 文档: This is pytools modulethe module is use MIT license MIT License Copyright (c) 202 ...

  10. Mybatis_day1

    mybatis框架的引入: JDBC里面 对功能实现的进一步封装,可以按照以下方式操作 Student s = new Student(id,name); util.save(s); ORM: obj ...