上一个博客提到ThreadLocal变量的基本使用方式,可以看出ThreadLocal是相对于每一个线程自己使用的本地变量,但是在实际的开发中,有这样的一种需求:父线程生成的变量需要传递到子线程中进行使用,那么在使用ThreadLocal似乎就解决不了这个问题,难道这个业务就没办法使用这个本地变量了吗?答案肯定是否定的,ThreadLocal有一个子类InheritableThreadLocal就是为了解决这个问题而产生的,使用这个变量就可以轻松的在子线程中依旧使用父线程中的本地变量。

--> InheritableThreadLocal基本的使用和分析:

如图上的代码所示,创建一个InheritableThreadLocal变量,并在main主线程中对该变量进行初始化,然后新建一个子线程直接获取刚刚初始化的变量值,代码执行的实际效果如下:

运行的结果可以看出,使用该变量子线程可以使用父线程中的本地变量值,那么具体的原因是什么呢?还是从源码一看究竟。

--> InheritableThreadLocal源码浅析:

首先看一下该类的目录结构,继承自ThreadLocal,并且重写了父类的方法:createMap(),getMap(),childValue();

其次当主线程中对该变量进行set操作的时候,和ThreadLocal一样会初始化一个ThreadLocalMap对实际的变量值进行存储,以ThreadLocal为key,值为value,如果有多个ThreadLocal变量也都是存储在这个Map中。该Map使用的是HashMap的原理进行数据的存储,但是和ThreadLocal有一点差别,因为其覆写了createMap的方法。

再将目光转移到线程Thread类中,可以看出Thread类维护了两个成员变量,ThreadLocal以及InheritableThreadLocal,数据类型都是ThreadLocalMap.这也就解释了为什么这个变量是线程私有的。但是如果要知道为什么父子线程的变量传递,那就继续看一下源码。当我们在主线程中开一个新的子线程的时候,开始会new一个新的Thread,具体的实现也就是在这里。

经过调用init方法可以看出,方法中存在对InheritableThreadLocal的操作。获取的父线程也就是当前实际开辟线程的主线程。

当父线程中的inheritableThreadLocal被赋值时,会将当前线程的inheritableThreadLocal变量进行createInheritedMap(),看一下这个方法的具体实现,它会继续调用ThreadLocalMap(parentMap),主要的目的是父线程的变量值赋值给子线程。这里直接改变的是Entry[],因为ThreadLocalMap只是一个类名,具体数据存储和操作是使用内部的数组搭配Hash算法和Entry内部类实现。

代码看到这里,对于为什么父线程的InheritableThreadLocal变量可以传递给子线程的原因应该已经清晰了。

--> InheritableThreadLocal和线程池搭配使用存在的问题:

首先创建一个线程池,设置其固定大小为1,调用这个线程池两次,在此之前分别对主线程中的InheritableThreadLocal进行赋值操作,观察运行的结果。

两次调用获取的值是一开始赋值的值,因为线程池中是缓存使用过的线程,当线程被重复调用的时候并没有再重新初始化init()线程,而是直接使用已经创建过的线程,所以这里的值并不会被再次操作。因为实际的项目中线程池的使用频率非常高,每一次从线程池中取出线程不能够直接使用之前缓存的变量,所以要解决这一个问题,网上大部分是推荐使用alibaba的开源项目transmittable-thread-local。具体可以自行了解,但是笔者认为需要弄清楚线程池和主线程以及本地变量直接的详细关系才可以更好的对这个项目有所理解。

--> 线程池扩展源码阅读

从构造线程池到使用线程池的过程:

1.创建一个线程池,其内部是对线程池基本的参数进行封装。

如上的构造方法可以看出,实际对线程进行初始化的接口是ThreadFactory,当线程池执行submit或者execute操作的时候,会对线程进行实际的构造,源码选用submit(Runable)进行阅读。

执行submit的时候首先进入方法newTaskFor();

实际是通过FutureTask对任务类进行封装,并且初始化的状态是NEW,代码跟进如下:

其次调用的是execute()方法:

如上的方法中,ctl相当于一个记录当前活跃线程数量的类,在ThreadLocalExcutor中被初始化,

所以在execute的代码中,首先判断正在执行任务的线程数量是否小于设置的线程数,来决定是否创建新的线程或者等待被执行。如果可以添加通过addWorker()进行线程的创建和添加,这也就是主线程和当前线程池中的线程传递本地变量的地方,因为这里会新建一个Thread。

详细的多线程源码分析笔者后续会继续准备。。。

InheritableThreadLocal——父线程传递本地变量到子线程的解决方式及分析相关推荐

  1. 【EventBus】事件通信框架 ( 发送事件 | 判断发布线程是否是主线程 | 子线程切换主线程 | 主线程切换子线程 )

    文章目录 前言 一.根据不同的线程模式进行不同的线程切换操作 二.完整代码示例 前言 发布线程发布事件之后 , 消息中心需要转发这些事件 , 并执行相应的订阅方法 ; 在转发的过程中 , 需要针对订阅 ...

  2. java 异常处理线程_转:Java子线程中的异常处理(通用)

    在普通的单线程程序中,捕获异常只需要通过try ... catch ... finally ...代码块就可以了.那么,在并发情况下,比如在父线程中启动了子线程,如何正确捕获子线程中的异常,从而进行相 ...

  3. Android线程之主线程向子线程发送消息

    和大家一起探讨Android线程已经有些日子了,谈的最多的就是如何把子线程中的数据发送给主线程进行处理,进行UI界面的更新,为什么要这样,请查阅之前的随笔.本篇我们就来讨论一下关于主线程向子线程如何发 ...

  4. handler回调主线程_Android使用Handler实现子线程与子线程、子线程与主线程之间通信...

    转载:https://blog.csdn.net/shaoenxiao/article/details/54561753 今天这篇文章只讲一下怎么使用Handler实现子线程与子线程之间.子线程与主线 ...

  5. vue父组件ajax改变数据,vue父组件传了变量给子组件,改变子组件的对象时,父组件也跟着改变...

    1.问题场景 首先我在父页面引用了一个子组件,当点击的时候我会传值给子组件 showItem(stepJsonItem: any) { var viewDlg = this.$refs.viewIte ...

  6. android Handler Message传递参数,handler子线程和主线程通讯

    创建Handler private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) { ...

  7. python主辅线程_python主线程捕获子线程的方法

    最近,在做一个项目时遇到的了一个问题,主线程无法捕获子线程中抛出的异常. 先看一个线程类的定义 ''''' Created on Oct 27, 2015 @author: wujz ''' impo ...

  8. android 开启子线程方法,android中开启子线程

    AndroidRuntime(673): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example/ ...

  9. 【Android 异步操作】Android 线程切换 ( 判定当前线程是否是主线程 | 子线程中执行主线程方法 | 主线程中执行子线程方法 )

    文章目录 一.判定当前线程是否是主线程 二.子线程中执行主线程方法 三.主线程中执行子线程方法 一.判定当前线程是否是主线程 在 Android 中 , 如果要判定当前线程是否是主线程 , 可以使用如 ...

最新文章

  1. T-SQL IN 谓词
  2. 十三、IntelliJ IDEA 中的版本控制介绍(下)
  3. android eclipse 下Device无设备问题解决
  4. 赛程一览 | 2019 上海国际创客大赛
  5. Jerry的WebClient UI 42篇原创文章合集
  6. 命令不识别_互助问答138期:GMM命令代码中如何识别年份国家及异方差检验问题...
  7. pdo_mysql未安装_php pdo_mysql未安装问题解决方法
  8. matlab 编程——一些细节、常犯错误的汇总
  9. 使用vs2019和pyinstaller将py文件打包成一个exe文件(含图标),pyinstaller安装失败解决方案
  10. python itertools卡死_python中的itertools的使用详解
  11. 为什么边缘概率密度是联合概率密度的积分_5.27005柏林联合VS美因茨
  12. 【Linux上分之路】第二篇:Linux硬件、磁盘结构和分区,Linux文件目录结构
  13. 小学计算机教案2018六年级,2017年小学六年级下册信息技术教学计划
  14. 都是S赛,为什么EDG夺冠公认“含金量最高”?
  15. linux ps-l命令详解,ps命令实用方法.ps -l ps -L详解
  16. 经典的足球明星广告--[困兽斗]
  17. java开发中常见的延时消息解决方案
  18. 【PMP认证考试之个人总结】第 7 章 项目质量管理
  19. Angular 入门(二)
  20. Servlet API中forward()与redirect()的区别?

热门文章

  1. 计算机如何编写VB五边形,VB 过程的调用 已知五边形各顶点的坐标,求其面积?...
  2. Word页码设置-减去封面
  3. 华为云-身份证识别-OCR
  4. Outlook 2010,突破附件大小限制
  5. iPhone开发Swift基础03 视频、网络请求
  6. Java springboot 跳出双层for循环
  7. spring定时任务(Scheduled)运行阻塞不执行/Redission分布式锁阻塞问题
  8. 怎样不使用迅雷而使用浏览器自带的下载进行下载
  9. 苹果手机配对手表总是显示服务器,苹果手机配对手表时一直显示正在验证ID~这是什么情况,在线等...
  10. HTTP有哪些优点和缺点