转自https://blog.csdn.net/hewenbo111/article/details/80487252

上一个博客提到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. ThreadLocal解析:父线程的本地变量不能传递到子线程详解

    众所周知,ThreadLocal类是java提供线程本地变量的工具类.但父线程的本地变量却不能被子线程使用,代码如下: public static void main(String[] args) { ...

  2. 每次调用fork()函数之后,父线程和创建出的子线程都是从fork()后开始执行

    Linux下多少个"-"将被打印: 1 2 3 4 5 6 7 8 int main(void){   int i;   for(i=0;i<4;i++){   fork() ...

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

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

  4. Android 使用handler实现线程间发送消息 (主线程 与 子线程之间)、(子线程 与 子线程之间)

    keyword:Android 使用handler实现线程间发送消息 (主线程 与 子线程之间).(子线程 与 子线程之间) 相信大家平时都有使用到异步线程往主线程(UI线程)发送消息的情况. 本文主 ...

  5. Java线程池主线程等待子线程执行完成

    今天讨论一个入门级的话题, 不然没东西更新对不起空间和域名~~ 工作总往往会遇到异步去执行某段逻辑, 然后先处理其他事情, 处理完后再把那段逻辑的处理结果进行汇总的产景, 这时候就需要使用线程了. 一 ...

  6. 线程:主线程、子线程 同步线程、异步线程 单线程、多线程 System.Threading与System.Windows.Threading

    入门-------------------------------------------------------------------------------- 概述与概念    一个C#程序开始 ...

  7. Java线程——线程习题(一)子线程执行10次后,主线程再运行5次,这样交替执行三遍

    题目:子线程执行10次后,主线程再运行5次,这样交替执行三遍 代码如下: package com.itheima.gan; /** * 子线程执行10次后,主线程再运行5次,这样交替执行三遍 * @a ...

  8. 跨线程传递栈变量带来异常指针Crash

    在手Q动漫的一份古老的代码中,现网发现少数crash,错误代码示例: char str[100] = "hello"; dispatch_async(dispatch_get_ma ...

  9. 线程本地变量ThreadLocal

    一.本地线程变量使用场景 并发应用的一个关键地方就是共享数据.如果你创建一个类对象,实现Runnable接口,然后多个Thread对象使用同样的Runnable对象,全部的线程都共享同样的属性.这意味 ...

随机推荐

  1. 拯救你丢失的精度——BigInteger和BigDecimal类(入门)

    第三阶段 JAVA常见对象的学习 BigInteger和BigDecimal类 BigInteger类 (一) 构造方法: //针对超过整数范围的运算(整数最大值:2147483647) BigInt ...

  2. 【洛谷】P5348 密码解锁

    [洛谷]P5348 密码解锁 很显然我们可以推导出这个式子 设\(a(m)\)为\(m\)位置的值 \[ \mu(m) = \sum_{m | d} a(d) \\ a(m) = \sum_{m|d} ...

  3. QT 打包exe

    QT打包主要方法: 1.把无措的代码进行Release编译 2.在运行完后,找到运行后生成的目录,以下是我的文件,名为result,运行类型有两种,一种是Debug,另一种是Release,我们需要的 ...

  4. 【第一季】CH08_FPGA_Button 按钮去抖动实验

    [第一季]CH08_FPGA_Button 按钮去抖动实验 按键的消抖,是指按键在闭合或松开的瞬间伴随着一连串的抖动,这样的抖动将直接影响设计系统的稳定性,降低响应灵敏度.因此,必须对抖动进行处理,即 ...

  5. 【第一季】CH07_FPGA_RunLED创建VIVADO工程实验

    [第一季]CH07_FPGA_RunLED创建VIVADO工程实验 7.1 硬件图片 先来熟悉一下开发板的硬件:LED部分及按钮部分 7.2 硬件原理图 PIN脚定义(讲解以MIZ702讲解,MIZ7 ...

  6. 简单分析synchronized不会锁泄漏的原因

    最近看到一句话:内部锁synchronized不会造成锁泄漏(Lock Leak). 锁泄漏是指一个线程获得某个锁以后,由于程序的错误.缺陷致使该锁一直没法被释放而导致其他线程一直无法获得该锁的现象. ...

  7. 【Android】二、HelloWorld

    1. 按照该网址写HelloWorld 例子   http://www.runoob.com/android/android-hello-world-example.html 2.点击 make pr ...

  8. SIP中的SDP offer/answer交换初探

    1.引言 SDP的offer/answer模型本身独立与于利用它的高层协议.SIP是使用offer/answer模型的应用之一.RFC 3264 定义了offer/answer模型,但没有规定使用哪个 ...

  9. Flutter 37: 图解 Flutter 基本动画 (一)

    小菜一直对动画不太熟悉,最近学习了一些关于动画的皮毛知识,网上资料很多,小菜按自己的理解整理一下. Animation Animation 可以生成动画过程中的值,生成的值并非单一的 double 也 ...

  10. Spring Cloud(O)服务的注册与发现(Eureka)

    一.微服务架构 1.1什么是分布式 不同模块部署在不同服务器上 作用:分布式解决网站高并发带来问题 1.2什么是集群 多台服务器部署相同应用构成一个集群 作用:通过负载均衡设备共同对外提供服务 1.3 ...