ThreadLocal解析:父线程的本地变量不能传递到子线程详解
众所周知,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解析:父线程的本地变量不能传递到子线程详解的更多相关文章
- 转载~kxcfzyk:Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解
Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解 多线程c语言linuxsemaphore条件变量 (本文的读者定位是了解Pthread常用多线程API和Pthread互斥锁 ...
- Linux线程体传递参数的方法详解
传递参数的两种方法 线程函数只有一个参数的情况:直接定义一个变量通过应用传给线程函数. 例子 #include #include using namespace std; pthread_t thre ...
- python theading线程开发与加锁、信号量、事件等详解
线程有2种调用方式,如下: 直接调用 import threading import time def sayhi(num): #定义每个线程要运行的函数 print("running on ...
- java打开本地应用程序(调用cmd)---Runtime用法详解
有时候我们需要借助java程序打开电脑自带的一些程序,可以直接打开或者借助cmd命令窗口打开一些常用的应用程序或者脚本,在cmd窗口执行的命令都可以通过这种方式运行. 例如: package cn.x ...
- Python3关于current_app传递给子线程
在学习Flask的时候,<Flask Web开发>这本书中有一个异步发送email的例子,其中用到了线程 from . import mail,create_app def send_as ...
- Java线程sleep,yield,join,wait方法详解
1.sleep() 当一个线程调用sleep方法后,他就会放弃cpu,转到阻塞队列,sleep(long millis)方法是Thread类中的静态方法,millis参数设定线程睡眠的时间,毫秒为单位 ...
- vue 父组件中的数据如何传递给子组件
父组件:<template> <div id="app"> <img src="./assets/logo.png"> &l ...
- 【Java 线程的深入研究4】ThreadPoolExecutor运转机制详解
hreadPoolExecutor机制 一.概述 1.ThreadPoolExecutor作为java.util.concurrent包对外提供基础实现,以内部线程池的形式对外提供管理任务执行,线程调 ...
- Android笔记(三十一)Android中线程之间的通信(三)子线程给主线程发送消息
先看简单示例:点击按钮,2s之后,TextView改变内容. package cn.lixyz.handlertest; import android.app.Activity; import and ...
随机推荐
- visual studio 2013 修改mvc5的视图模板
C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\Extensions\Microsoft\Web\Mvc\Scaffol ...
- H3C OSI参考模型层次结构
- PyTorch之前向传播函数自动调用forward
参考:1. pytorch学习笔记(九):PyTorch结构介绍 2.pytorch学习笔记(七):pytorch hook 和 关于pytorch backward过程的理解 3.Pytorch入门 ...
- hdu 4394 Digital Square(bfs)
Digital Square Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
- tsung测试xmpp遇到no_free_userid
tsung里面可以配置xmpp的参数,设置一下 <option type="ts_jabber" name="userid_max" value=&quo ...
- Mockito 使用
1. 算术测试类 package com.smart.test.mockito; public interface Calculator { public int add(int a, int b); ...
- 原 在windows上创建文件名以“.”开头的文件
在实际项目开发中,我们会经常遇到以”.”开头的配置文件.例如我们如果需要通过配置文件给npm更换源就需要更改.npmrc文件,然而还有很多友友还不清楚如何创建一个类似的文件,今天就和大家分享下这个小技 ...
- Educational Codeforces Round 54 (Rated for Div. 2) D Edge Deletion (SPFA + bfs)
题目大意:给定你一个包含n个点m条边的无向图,现在最多在图中保留k条边,问怎么删除多的边,使得图中良好的节点数最多,求出保留在图中的边的数量和编号. 良好的节点定义为:删除某条边后该点到点1的最短距离 ...
- Activiti工作流引擎学习(一)
1.部署对象和流程定义相关表:RepositoryService act_re_deployment: 部署对象表:一次部署的多个文件的信息,对于不需要的流程可以删除和修改 act_re_procde ...
- js实现new
function New(fn,...args){ let obj={} obj.__proto__=fn.prototype let result=fn.apply(obj,args) if(typ ...