一、问题的提出

在系统开发过程中常使用ThreadLocal进行传递日志的RequestId,由此来获取整条请求链路。然而当线程中开启了其他的线程,此时ThreadLocal里面的数据将会出现无法获取/读取错乱,甚至还可能会存在内存泄漏等问题,下面用代码来演示一下这个问题。

普通代码示例:

并行流代码示例:

二、问题的解决

ThreadLocal的子类InheritableThreadLocal其实已经帮我们处理好了,通过这个组件可以实现父子线程之间的数据传递,在子线程中能够父线程中的ThreadLocal本地变量。

三、源码的分析

可以看出InheritableThreadLocal继承自ThreadLocal,并重写了三个相关方法。

再回来过来看ThreadLocal的源码:

我们发现InheritableThreadLocal中createMap,以及getMap方法处理的对象不一样了,其中在ThreadLocal中处理的是threadLocals,而InheritableThreadLocal中的是inheritableThreadLocals,我们再顺藤摸瓜看一下Thread对象的处理,其中在init源码中我们看到这么一段代码:

代码的意思是在Thread获取先父亲线程parent(即要创建子线程的当前这个线程)。当父亲线程中对inherThreadLocals进行了赋值,就会把当前线程的本地变量(也就是父线程的inherThreadLocals)进行createInheritedMap方法操作。查看源码createInheritedMap方法,源码可知此操作就是将赋线程的threadLocalMap传递给子线程。

我们写个代码测试一下:

看起来似乎真的是解决了我们无法传递的问题。

四、真的就这么美好么?我们来和线程池搭配一下

测试结果显示两次赋值,得到的结果还是第一次的值!为什么?

其实原因也很简单,我们的线程池会缓存使用过的线程。当线程需要被重复利用的时候,并不会再重新执行init()初始化方法,而是直接使用已经创建过的线程,所以这里的值不会二次产生变化,那么该怎么做到真正的父子线程数据传递呢?

五、真正的解决方案:阿里的transmittable-thread-local了解一下

JDK的InheritableThreadLocal类可以完成父线程到子线程的值传递。但对于使用线程池等会池化复用线程的组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的ThreadLocal值传递已经没有意义,应用需要的实际上是把任务提交给线程池时的ThreadLocal值传递到任务执行时。

首先分析一下最核心的类:TransmittableThreadLocal

首先TransmittableThreadLocal继承自InheritableThreadLocal,这样可以在不破坏原有InheritableThreadLocal特性的情况下,还能充分使用Thread线程创建过程中执行init方法,从而达到父子线程传递数据的目的。

这里有一个很重要的变量holder:源码如下

1. holder中存放的是InheritableThreadLocal本地变量。

2. WeakHashMap支持存放空置。

主要的几个相关方法:源码如下

1. get方法调用时,先获取父亲的相关数据判断是否有数据,然后在holder中把自身也给加进去。

2. set方法调用时,先在父亲中设置,再本地判断是holder否为删除或者是新增数据。

3. remove调用时,先删除自身,再删除父亲中的数据,删除也是直接以自身this作为变量Key。

采用包装的形式来处理线程池中的线程不会执行初始化的问题,源码如下:

1. 先取得holder。

2. 备份线程本地数据

3. run原先的方法

4. 还原线程本地数据

备份方法:

1. 先获取holder中的数据

2. 进行迭代,数据在captured中不存在,但是holder中存在,说明是后来加进去的,进行删除。

3. 再将captured设置到当前线程中。

还原方法:

1. 先获取holder中的数据

2. backup中不存在,holder中存在,说明是后面加进去的,进行删除还原操作。

3. 再将backup设置到当前线程中。

下面是几个典型场景例子。

1. 分布式跟踪系统

2. 日志收集记录系统上下文

3. 应用容器或上层框架跨应用代码给下层SDK传递信息

项目地址:https://github.com/alibaba/transmittable-thread-local

ThreadLocal父子线程之间的数据传递问题的更多相关文章

  1. Angular06 组件、模块、父子组件之间的数据传递

    1 创建组件 进入到angular项目的根目录,执行如下命令 ng g component test-component 注意:执行完上述命令后在angular项目的src/app文件夹下就会多出一个 ...

  2. Vue 编程之路(一)——父子组件之间的数据传递

    最近公司的一个项目中使用 Vue 2.0 + element UI 实现一个后台管理系统的前端部分,属于商城类型.其中部分页面是数据管理页,所以有很多可以复用的表格,故引入自定义组件.在这里分享一下开 ...

  3. 解决vue不相关组件之间的数据传递----vuex的学习笔记,解决报错this.$store.commit is not a function

    Vue的项目中,如果项目简单, 父子组件之间的数据传递可以使用  props 或者 $emit 等方式 进行传递 但是如果是大中型项目中,很多时候都需要在不相关的平行组件之间传递数据,并且很多数据需要 ...

  4. Backbone中父子view之间的值传递

    backbone中,使用最多的莫过于在view中进行操作,如模板的渲染以及事件函数的定义.为了提高代码的可维护性,一般地我们会写多个视图即view,将界面按照功能的不同进行模块化划分,模块与view一 ...

  5. vuejs组件交互 - 01 - 父子组件之间的数据交互

    父子组件之间的数据交互遵循: props down - 子组件通过props接受父组件的数据 events up - 父组件监听子组件$emit的事件来操作数据 示例 子组件的点击事件函数中$emit ...

  6. ASP.NET MVC3中Controller与View之间的数据传递

    在ASP.NET MVC中,经常会在Controller与View之间传递数据,因此,熟练.灵活的掌握这两层之间的数据传递方法就非常重要.本文从两个方面进行探讨: 一.  Controller向Vie ...

  7. Activity之间的数据传递

    最常用的Activity之间的数据传递. btnStartAty1.setOnClickListener(new View.OnClickListener() { @Override public v ...

  8. ASP.NET MVC 之控制器与视图之间的数据传递

    今天,我们来谈谈控制器与视图之间的数据传递. 数据传递,指的是视图与控制器之间的交互,包括两个方向上的数据交互,一个是把控制器的数据传到视图中,在视图中如何显示数据,一个是把视图数据传递到控制器中, ...

  9. (转载)Javascript操作表单之间的数据传递

    (转载)http://www.aspxhome.com/javascript/skills/200710/214825.htm 今天有朋友问我关于用JAVASCRIPT来进行页面各表单之间的数据传递的 ...

随机推荐

  1. IIS遇到过的问题

    1. IIS的一个莫名错误Server Application Unavailable http://www.kesion.com/zzcd/asp/aspjq/474.html 新打开这个服务ASP ...

  2. 8月清北学堂培训 Day2

    今天是赵和旭老师的讲授~ 背包 dp 模型 背包 dp 一般是给出一些“物品”,每个物品具有一些价值参数和花费参数,要求 在满足花费限制下最大化价值或者方案数. 最简单几种类型以及模型: 0/1背包: ...

  3. Python学习日记(六)——内置函数和文件操作(lambda)

    lambda表达式 学习条件运算时,对于简单的 if else 语句,可以使用三元运算来表示,即: # 普通条件语句 if 1 == 1: name = 'prime' else: name = 'c ...

  4. 图解Linux进程间通信实现原理(1)

    为Linux应用程序的开发人员,对Linux的进程间通信方式肯定是了如指掌,平时的开发中应该会大量的使用到.当你迅速的在键盘上按下[CTRL+C]终止掉一个正在运行中的命令时,你有没有仔细的思考过背后 ...

  5. docker运行puppeteer出现Page crash解决方案

    Docker默认文件空间64MB.如果puppeteer运行的时候超过这个内存就出现了.Page crash.可以使用docker run --shm-size=256m指定一个更大的内存即可.

  6. spring boot 原理解析一(spring boot 基础特征)

    spring boot 提供了完整的介绍 文档:https://docs.spring.io/spring-boot/docs/2.2.2.RELEASE/reference/html/documen ...

  7. nginx - nginx下配置thinkphp5

    首先tp5的访问目录指向到webroot/public文件夹中.thinkphp的url访问:http://serverName/index.php(或者其它应用入口文件)/模块/控制器/操作/[参数 ...

  8. SQL-W3School-高级:SQL 通配符

    ylbtech-SQL-W3School-高级:SQL 通配符 1.返回顶部 1. 在搜索数据库中的数据时,您可以使用 SQL 通配符. SQL 通配符 在搜索数据库中的数据时,SQL 通配符可以替代 ...

  9. 思科常见的防病毒ACL

    SW(config-ext-nacl)# 10 deny tcp any any eq 27665 SW(config-ext-nacl)# 20 deny tcp any any eq 16660 ...

  10. [Scikit-learn] 1.1 Generalized Linear Models - Comparing various online solvers

    数据集分割 一.Online learning for 手写识别 From: Comparing various online solvers An example showing how diffe ...