本博客系列是学习并发编程过程中的记录总结。由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅。

并发编程系列博客传送门


引子

  1. public class InheritableThreadLocalDemo {
  2. private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
  3. public static void main(String[] args) {
  4. threadLocal.set("mainThread");
  5. System.out.println("value:"+threadLocal.get());
  6. Thread thread = new Thread(new Runnable() {
  7. @Override
  8. public void run() {
  9. String value = threadLocal.get();
  10. System.out.println("value:"+value);
  11. }
  12. });
  13. thread.start();
  14. }
  15. }

上面代码中在主线程中设置了一个ThreadLocal变量,并将其值设置为mainThread。然后有在主线程中开启了一个子线程thread,并试图获取在主线程中set的ThreadLocal变量的值。但是结果如下:

  1. value:mainThread
  2. value:null

通过前面的文章介绍,对于上面的结果我们也就非常容易理解了。每个线程都会有一个自己的ThreadLocalMap,所以子线程在调用get方法拿值的时候其实访问的是自己的ThreadLocalMap,这个Map和主线程的Map是两个不同的对象,所以肯定是拿不到值的。

那么Java中有没有类似的对象能实现上面的功能呢?有,InheritableThreadLocal就能实现这样的功能,这个类能让子线程继承父线程中已经设置的ThreadLocal值。

InheritableThreadLocal简单使用

还是以上面的列子为列,我们只需要将ThreadLocal变成InheritableThreadLocal就行了。

  1. public class InheritableThreadLocalDemo {
  2. private static InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
  3. public static void main(String[] args) {
  4. threadLocal.set("mainThread");
  5. System.out.println("value:"+threadLocal.get());
  6. Thread thread = new Thread(new Runnable() {
  7. @Override
  8. public void run() {
  9. String value = threadLocal.get();
  10. System.out.println("value:"+value);
  11. }
  12. });
  13. thread.start();
  14. }
  15. }

执行结果如下:

  1. value:mainThread
  2. value:mainThread

InheritableThreadLocal原理分析

先看下InheritableThreadLocal的源代码:

  1. public class InheritableThreadLocal<T> extends ThreadLocal<T> {
  2. protected T childValue(T parentValue) {
  3. return parentValue;
  4. }
  5. ThreadLocalMap getMap(Thread t) {
  6. return t.inheritableThreadLocals;
  7. }
  8. void createMap(Thread t, T firstValue) {
  9. t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
  10. }
  11. }

这个类继承了ThreadLocal,并且重写了getMap和createMap方法,区别就是将 ThreadLocal 中的 threadLocals 换成了 inheritableThreadLocals,这两个变量都是ThreadLocalMap类型,并且都是Thread类的属性。

下面就一步步来看下InheritableThreadLocal为什么能拿到父线程中的ThreadLocal值。

step1:InheritableThreadLocal获取值先调用了get方法,所以我们直接看看get方法都做了些啥。

  1. public T get() {
  2. Thread t = Thread.currentThread();
  3. ThreadLocalMap map = getMap(t);
  4. if (map != null) {
  5. ThreadLocalMap.Entry e = map.getEntry(this);
  6. if (e != null) {
  7. @SuppressWarnings("unchecked")
  8. T result = (T)e.value;
  9. return result;
  10. }
  11. }
  12. return setInitialValue();
  13. }

从上面的代码可以看出,get方法和ThreadLocal中是一样的,唯一有区别的就是其中的getMap方法重写了,返回的是inheritableThreadLocals属性。这个属性也是一个ThreadLocalMap类型的变量。那么从这边就可以推断出来:肯定是在某处将父线程中的ThreadLocal值赋值到了子线程的inheritableThreadLocals中。

step2:在源代码中搜索哪些地方使用到了inheritableThreadLocals这个属性,最后找到这段代码:

  1. private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc) {
  2. if (name == null) {
  3. throw new NullPointerException("name cannot be null");
  4. }
  5. this.name = name.toCharArray();
  6. Thread parent = currentThread();
  7. SecurityManager security = System.getSecurityManager();
  8. if (g == null) {
  9. if (security != null) {
  10. g = security.getThreadGroup();
  11. }
  12. if (g == null) {
  13. g = parent.getThreadGroup();
  14. }
  15. }
  16. g.checkAccess();
  17. if (security != null) {
  18. if (isCCLOverridden(getClass())) {
  19. security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
  20. }
  21. }
  22. g.addUnstarted();
  23. this.group = g;
  24. this.daemon = parent.isDaemon();
  25. this.priority = parent.getPriority();
  26. if (security == null || isCCLOverridden(parent.getClass()))
  27. this.contextClassLoader = parent.getContextClassLoader();
  28. else
  29. this.contextClassLoader = parent.contextClassLoader;
  30. this.inheritedAccessControlContext =
  31. acc != null ? acc : AccessController.getContext();
  32. this.target = target;
  33. setPriority(priority);
  34. //1. 这边先判断了父线程中inheritableThreadLocals属性是否为空,不为空的话就复制给子线程
  35. if (parent.inheritableThreadLocals != null)
  36. this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
  37. /* Stash the specified stack size in case the VM cares */
  38. this.stackSize = stackSize;
  39. /* Set thread ID */
  40. tid = nextThreadID();
  41. }

上面的代码印证了我们的猜想。需要注意的是一旦子线程被创建以后,再操作父线程中的ThreadLocal变量,那么子线程是不能感知的。因为父线程和子线程还是拥有各自的ThreadLocalMap,只是在创建子线程的“一刹那”将父线程的ThreadLocalMap复制给子线程,后续两者就没啥关系了。

【并发编程】ThreadLocal的兄弟InheritableThreadLocal的更多相关文章

  1. 9.并发编程--ThreadLocal

    并发编程--ThreadLocal 1. ThreadLocal : * 线程局部变量,是一种多个线程间并发访问变量的解决方案. * 与其使用synchronized等加锁的方式,ThreadLoca ...

  2. 并发编程之详解InheritableThreadLocal类原理

    [本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 在Java并发编 ...

  3. java并发编程(九)ThreadLocal & InheritableThreadLocal

    参考文档: https://blog.csdn.net/u012834750/article/details/71646700 threadlocal内存泄漏:http://www.importnew ...

  4. 并发编程 01—— ThreadLocal

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  5. Java并发编程:ThreadLocal

    Java并发编程:深入剖析ThreadLocal   Java并发编程:深入剖析ThreadLocal 想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使用 ...

  6. Java并发编程--理解ThreadLocal

    另一篇博文:Hibernet中的ThreadLocal使用 http://www.cnblogs.com/gnivor/p/4440776.html 本文参考:http://blog.csdn.net ...

  7. Java并发编程:深入剖析ThreadLocal(转载)

    Java并发编程:深入剖析ThreadLocal(转载) 原文链接:Java并发编程:深入剖析ThreadLocal 想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨下ThreadL ...

  8. 并发编程(四):ThreadLocal从源码分析总结到内存泄漏

    一.目录      1.ThreadLocal是什么?有什么用?      2.ThreadLocal源码简要总结?      3.ThreadLocal为什么会导致内存泄漏? 二.ThreadLoc ...

  9. (转)Java并发编程:深入剖析ThreadLocal

    Java并发编程:深入剖析ThreadLoca Java并发编程:深入剖析ThreadLocal 说下自己的理解:使用ThreadLocal能够实现空间换时间,重在理解ThreadLocal是如何复制 ...

随机推荐

  1. [开源]基于goapp+xterm实现webssh-网页上的SSH终端(golang)

    简析 基于goapp+xterm实现webssh-网页上的SSH终端. 开源地址见文末. 特性 在网页上实现一个SSH终端.从而无需Xshell之类的模拟终端工具进行SSH连接. 可以对交互命令进行审 ...

  2. rpm 方式安装java

    1.rpm下载地址 http://www.oracle.com/technetwork/java/javase/downloads/index.html 2.如果有安装openjdk 则卸载 #### ...

  3. C# - VS2019 通过DataGridView实现对Oracle数据表的增删改查

    前言 通过VS2019建立WinFrm应用程序,搭建桌面程序后,通过封装数据库操作OracleHelper类和业务逻辑操作OracleSQL类,进而通过DataGridView实现对Oracle数据表 ...

  4. python之小木马(文件上传,下载,调用命令行,按键监控记录)

    window版 服务端: 开启两个线程,一个用来接收客户端的输入,一个用来监控服务端键盘的记录 客户端: get 文件(下载)put 文件(上传) window下cmd命令执行结果会直接打印出来,ke ...

  5. C#动态多态性的理解

    C#动态多态性是通过抽象类和虚方法实现的. 抽象类的理解 用关键字abstract创建抽象类,用于提供接口的部分类的实现(理解:接口不能提供实现,抽象类中可以有实现,接口与抽象类一起使用,可以达到父类 ...

  6. windows系统cmd命令行窗口查看端口占用情况

    # 查看所有在用端口 netstat -ano # 查看指定端口 netstat -ano | findstr 8899 # 结束该进程:taskkill /f /t /im javaw.exe:或者 ...

  7. UNIX env查找技巧

    在一些UNIX系统上,也许可以避免硬编码Python解释器的路径,而可以在文件特定的第一行注释中这样写: #!/usr/bin/env python ... script goes here ... ...

  8. 读取JDK API文档,并根据单词出现频率排序

    1,拿到 API 文档 登录 https://docs.oracle.com/javase/8/docs/api/ , 选中特定的类,然后 copy 其中的内容, 放入 TXT 文件中 , 2,读取T ...

  9. Vue_声明周期

    Vue生命周期 在vue2.0的时候,声明钩子发生了改变,具体有八个 <!-- HTML部分 --> <div id="app"> <div>{ ...

  10. Java中的工具类究竟如何命名?

    先来几个例子 JDK自带工具类 Arrays.asList(); Objects.equals(); Collections.sort(); Spring框架工具类 StringUtils.isEmp ...