Java InheritableThreadLocal使用示例详解
作者:程序员自由之路 发布时间:2023-04-17 20:18:12
引子
public class InheritableThreadLocalDemo {
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
threadLocal.set("mainThread");
System.out.println("value:"+threadLocal.get());
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
String value = threadLocal.get();
System.out.println("value:"+value);
}
});
thread.start();
}
}
上面代码中在主线程中设置了一个ThreadLocal变量,并将其值设置为mainThread
。然后有在主线程中开启了一个子线程thread
,并试图获取在主线程中set的ThreadLocal变量的值。但是结果如下:
value:mainThread
value:null
通过前面的文章介绍,对于上面的结果我们也就非常容易理解了。每个线程都会有一个自己的ThreadLocalMap,所以子线程在调用get方法拿值的时候其实访问的是自己的ThreadLocalMap,这个Map和主线程的Map是两个不同的对象,所以肯定是拿不到值的。
那么Java中有没有类似的对象能实现上面的功能呢?有,InheritableThreadLocal
就能实现这样的功能,这个类能让子线程继承父线程中已经设置的ThreadLocal值。
InheritableThreadLocal简单使用
还是以上面的列子为列,我们只需要将ThreadLocal变成InheritableThreadLocal就行了。
public class InheritableThreadLocalDemo {
private static InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
public static void main(String[] args) {
threadLocal.set("mainThread");
System.out.println("value:"+threadLocal.get());
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
String value = threadLocal.get();
System.out.println("value:"+value);
}
});
thread.start();
}
}
执行结果如下:
value:mainThread
value:mainThread
InheritableThreadLocal原理分析
先看下InheritableThreadLocal的源代码:
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
protected T childValue(T parentValue) {
return parentValue;
}
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
这个类继承了ThreadLocal,并且重写了getMap和createMap方法,区别就是将 ThreadLocal 中的 threadLocals 换成了 inheritableThreadLocals,这两个变量都是ThreadLocalMap类型,并且都是Thread类的属性。
下面就一步步来看下InheritableThreadLocal为什么能拿到父线程中的ThreadLocal值。
step1:InheritableThreadLocal获取值先调用了get方法,所以我们直接看看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方法和ThreadLocal中是一样的,唯一有区别的就是其中的getMap方法重写了,返回的是inheritableThreadLocals属性。这个属性也是一个ThreadLocalMap类型的变量。那么从这边就可以推断出来:肯定是在某处将父线程中的ThreadLocal值赋值到了子线程的inheritableThreadLocals中。
step2:在源代码中搜索哪些地方使用到了inheritableThreadLocals
这个属性,最后找到这段代码:
private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name.toCharArray();
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
if (security != null) {
g = security.getThreadGroup();
}
if (g == null) {
g = parent.getThreadGroup();
}
}
g.checkAccess();
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
//1. 这边先判断了父线程中inheritableThreadLocals属性是否为空,不为空的话就复制给子线程
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
上面的代码印证了我们的猜想。需要注意的是一旦子线程被创建以后,再操作父线程中的ThreadLocal变量,那么子线程是不能感知的。因为父线程和子线程还是拥有各自的ThreadLocalMap,只是在创建子线程的“一刹那”将父线程的ThreadLocalMap复制给子线程,后续两者就没啥关系了。
来源:https://www.cnblogs.com/54chensongxia/p/12015443.html


猜你喜欢
- 用DataFormatString格式化GridView在 GridView里面显示数据,要显示的数据有好多位小数,就想让它只显示两位小数,
- 本文实例讲述了Java计算文本MD5加密值的方法。分享给大家供大家参考,具体如下:java计算文本MD5值,用于加密import java.
- 前言表之间的关系有几种:一对多、多对一、 一对一、多对多在多对一关系中,把多的部分拆成一个一个对象其实就是一对一关系,如账户和用户是多对一关
- 本文实例为大家分享了SpringBoot获取yml和properties配置文件的具体代码,供大家参考,具体内容如下(一)yml配置文件:p
- RMI 介绍RMI (Remote Method Invocation) 模型是一种分布式对象应用,使用 RMI 技术可以使一个 JVM 中
- 目录1、synchronized锁的底层实现原理2、基于synchronized实现单例模式3、利用类加载实现单例模式(饿汉模式)1、syn
- 前言通过本文你可以了解List的五种遍历方式及各自性能和foreach及Iterator的实现,加深对ArrayList和LinkedLis
- 从今天开始写关于C#的系列文章,本篇文章主要讲解C#中的委托使用。委托其实就是一种数据类型,和int,string是一样的概念。如果要把一个
- 本文实例讲述了C#将HashTable中键列表或值列表复制到一维数组的方法。分享给大家供大家参考。具体如下:下面的示例说明如何将 Hasht
- 本文所述实例主要实现WPF项目中C#改变DataGrid某一行和单元格颜色的功能。分享给大家供大家参考。具体方法如下:如果要改变DataGr
- 先直接看看效果吧初始情况 点击一个作为标记 再次点击后删除 3.这里还要感谢前辈的代码作为参考,毕竟以前也没有写过关于日历方面的东西,别人确
- //计算耗时任务所需的秒数public int GetTimeSpan(DateTime dtStart, DateTime dtEnd){
- 一、问题最近在做代码重构,代码工程采用了Controller/Service/Dao分层架构,Dao层使用了Mybatis-Plus框架。在
- Android安全加密专题文章索引Android安全加密:对称加密Android安全加密:非对称加密Android安全加密:消息摘要Mess
- Java BigDecimal的坑采坑处 BigDecimal bd =new BigDecimal(0.1);
- 本文中所有效果通过ItemDecoration实现,通过此实现的可以与业务解耦,让RecyclerView的模板更加简洁,不关心任何辅助性u
- 最近在研究springboot实现FastJson解析json数据的方法,那么今天也算个学习笔记吧!添加jar包:<dependenc
- Dagger2注入框架原理简要分析使用Dagger2需要的依赖:implementation 'com.google.dagger:
- 一、直接看效果二、直接上代码1.自定义控件部分package com.susan.project.myapplication;import
- 前言定义一个工厂类,他可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类在简单工厂模式中用于被创建实例的方法通常为静态(s