众所周知,ThreadLocal类是java提供线程本地变量的工具类。但父线程的本地变量却不能被子线程使用,代码如下:

 public static void main(String[] args) {
ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("abc");
System.out.println("父线程:"+threadLocal.get());
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.print("子线程:"+threadLocal.get());
}
});
t1.start();
}

运行结果如下:

至于原因呢,得先了解ThreadLocal存储的变量是怎么存储的。首先,让我们先看看Thread类的源码:在thread类中有声明这么一个成员变量——threadLocals

ThreadLocal.ThreadLocalMap threadLocals = null;

根据定义可以看出,这是ThreadLocal类里的静态内部类,它的结构是Map结构,以键值对的形式存储值。key值就是当前ThreadLocal类的实例,value值就是当前线程的本地变量。所以线程的本地变量是存在线程实例当中的,而不是存在ThreadLocal中。ThreadLocal只是一个工具类,体现在当ThreadLocal实例调用set()方法时,会将当前线程的threadLocals变量实例化。以下是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);
}

可以看到,先是获取当前线程的实例,再获取线程的成员变量threadLocals,如果threadLocals已经实例化,就直接以当前ThreadLocal类为key,存储的本地变量为value,存进threadLocals中。所以线程本地变量是存储在线程中的。如果threadLocals未实例化,则调用createMap()方法,该方法会调用ThreadLocalMap的构造方法,初始化一个以当前ThreadLocal类为key,存储的本地变量为value的Map。该Map是类似HashMap的,感兴趣的话可以继续往下看源码,此处不做扩展。

回到最开始的问题,子线程调用ThreadLocal类的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();
}

此时,会产生两个分支,一个是通过子线程的ThreadLocalMap的getEntry()方法获取Entry并返回value值,但由于子线程的ThreadLocalMap!=父线程的ThreadLocalMap,所以获取不到父线程的本地变量。另一个分支是返回setInitialValue()的返回值,setInitialValue()方法源码如下:

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);
return value;
}

可以看到value会默认为null,如果子线程的ThreadLocalMap已经实例化,则直接以ThreadLocal实例为key,null为value存进ThreadLocalMap中,否则就创建一个同上所述的ThreadLocalMap。

引发出的问题:ThreadLocal类是弱引用,一次GC后会为null,当key为null时,value值却还存在内存中,造成内存泄漏。所以ThreadLocal类最后一定要执行remove()方法。在GC之前将内存释放。

综上,第一次写博客,写得不好或者有错误的地方,请指正。

ThreadLocal解析:父线程的本地变量不能传递到子线程详解的更多相关文章

  1. 转载~kxcfzyk:Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解

    Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解   多线程c语言linuxsemaphore条件变量 (本文的读者定位是了解Pthread常用多线程API和Pthread互斥锁 ...

  2. Linux线程体传递参数的方法详解

    传递参数的两种方法 线程函数只有一个参数的情况:直接定义一个变量通过应用传给线程函数. 例子 #include #include using namespace std; pthread_t thre ...

  3. python theading线程开发与加锁、信号量、事件等详解

    线程有2种调用方式,如下: 直接调用 import threading import time def sayhi(num): #定义每个线程要运行的函数 print("running on ...

  4. java打开本地应用程序(调用cmd)---Runtime用法详解

    有时候我们需要借助java程序打开电脑自带的一些程序,可以直接打开或者借助cmd命令窗口打开一些常用的应用程序或者脚本,在cmd窗口执行的命令都可以通过这种方式运行. 例如: package cn.x ...

  5. Python3关于current_app传递给子线程

    在学习Flask的时候,<Flask Web开发>这本书中有一个异步发送email的例子,其中用到了线程 from . import mail,create_app def send_as ...

  6. Java线程sleep,yield,join,wait方法详解

    1.sleep() 当一个线程调用sleep方法后,他就会放弃cpu,转到阻塞队列,sleep(long millis)方法是Thread类中的静态方法,millis参数设定线程睡眠的时间,毫秒为单位 ...

  7. vue 父组件中的数据如何传递给子组件

    父组件:<template> <div id="app"> <img src="./assets/logo.png"> &l ...

  8. 【Java 线程的深入研究4】ThreadPoolExecutor运转机制详解

    hreadPoolExecutor机制 一.概述 1.ThreadPoolExecutor作为java.util.concurrent包对外提供基础实现,以内部线程池的形式对外提供管理任务执行,线程调 ...

  9. Android笔记(三十一)Android中线程之间的通信(三)子线程给主线程发送消息

    先看简单示例:点击按钮,2s之后,TextView改变内容. package cn.lixyz.handlertest; import android.app.Activity; import and ...

随机推荐

  1. 根据User Agent参数的各个字段Mozilla/5.0/4.0-AppleWebKit/Chrome/Safari/Firefox/Opera/MSIE来确定/判断客户端使用什么浏览器

    下面给你一一解答以及给你介绍: //Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/533.21.1 (KHTML, like ...

  2. rowStyle设置Bootstrap Table行样式

    日常开发中我们通常会用到隔行变色来美化表格,也会根据每行的数据显示特定的背景颜色,如果库存低于100的行显示红色背景 CSS样式 <style> .bg-blue { background ...

  3. javascript 元素的大小

    1.偏移量 元素的可见大小由其高度.宽度决定,包括所有内边距.滚动条和边框大小(不包含外边距).通过下列4个属性可以获取元素的偏移量: offsetHeight: offsetWidth: offse ...

  4. 横向tab计算滚动位置

    React横向滚动计算 class Footer extends React.Component { handleClick(e) { const offset = 150; // 指定偏移量 thi ...

  5. Python--day21--包

    包: 包是一种通过使用‘.模块名’来组织python模块名称空间的方式. 1. 无论是import形式还是from...import形式,凡是在导入语句中(而不是在使用时)遇到带点的,都要第一时间提高 ...

  6. H3C PPP基本概念

  7. Python--day46--MySQL自定义函数

    1,mysql自定义函数(mysql函数会降低查找速度,使用 了函数的行和列不能再用索引查找了,这样对性能要求高的需求就不能这样写) 函数调用 select f(参数1,参数2): 对函数进行调用:

  8. H3C 示例:根据主机地址数划分子网

  9. jquery自己写的幻灯片插件,好用不解释

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  10. 【39.87%】【BZOJ 1880】[Sdoi2009]Elaxia的路线

    Time Limit: 4 Sec  Memory Limit: 64 MB Submit: 1041  Solved: 415 [Submit][Status][Discuss] Descripti ...