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,只是在创建子线程的&ldquo;一刹那&rdquo;将父线程的ThreadLocalMap复制给子线程,后续两者就没啥关系了。

来源:https://www.cnblogs.com/54chensongxia/p/12015443.html

标签:Java,InheritableThreadLocal
0
投稿

猜你喜欢

  • AndroidStudio替换项目图标ic_launcher操作

    2023-03-20 09:48:07
  • tcp、udp、ip协议分析_动力节点Java学院整理

    2023-05-17 18:00:17
  • Android弹窗ListPopupWindow的简单应用详解

    2021-08-04 19:42:52
  • C#使用Ado.Net更新和添加数据到Excel表格的方法

    2022-10-02 01:27:24
  • Java 添加、读取和删除 Excel 批注的操作代码

    2023-10-28 21:55:36
  • 详解Spring Data JPA动态条件查询的写法

    2022-03-17 18:26:06
  • C++链表节点的添加和删除介绍

    2023-08-23 02:32:09
  • C#中值类型和引用类型解析

    2023-02-10 22:41:33
  • C# 大小写转换(金额)实例代码

    2021-07-06 00:33:56
  • Android开发之TabHost选项卡及相关疑难解决方法

    2022-01-10 06:30:57
  • Android studio编写简单的手电筒APP

    2023-11-29 18:43:01
  • C#使用Socket实现服务器与多个客户端通信(简单的聊天系统)

    2022-03-08 23:48:07
  • Java 回调函数深入理解

    2023-11-01 17:32:04
  • Android打包篇:Android Studio将代码打包成jar包教程

    2022-08-14 06:22:20
  • Java实现超市会员管理系统

    2021-10-01 08:39:28
  • Minio与SpringBoot使用okhttp3问题解决

    2021-06-25 19:17:08
  • C#编程调用Cards.dll实现图形化发牌功能示例

    2022-10-24 12:02:54
  • Android Application级别自定义Toast

    2022-01-02 03:41:45
  • Android简单获取经纬度的方法

    2021-07-28 05:26:47
  • Android仿微信键盘切换效果

    2022-05-01 08:06:41
  • asp之家 软件编程 m.aspxhome.com