
Java中的
threadlocal绑定到一个线程. 您在一个线程中设置的值在另一个线程中不可用. 如果在threadlocal的并行线程中创建了新的子线程,则无法传递和共享其中的值(请考虑为什么先看不起). 这是透明传输的问题.
该值程之间透明地传输. 您可以将其视为错误. 这些问题通常更容易被掩盖,但是当问题暴露出来时,脾气就会越来越流行,使人们感到困惑和怀疑自己的生活.
作为代码的掌舵人,我们一定不能容忍这种问题. 本文适合于仔细研究. 我们以三个示例来说明通过编码方法解决此类错误的一般方法,以期实现的效果. 对于从事基础设施建设的学生来说,这是必不可少的知识点.
1,普通线程的ThreadLocal透明传输问题
2. sl4j MDC组件中的ThreadLocal透明传输问题
3. Hystrix组件的透明传输
因为涉及更多代码,所以xjjdog将这三个示例的代码放在github上. 如果您想深入研究,可以下载并调试它.
https://github.com/xjjdog/example-pass-through
为了更直观地理解,下面显示了一个异常代码.


上面的代码在主线程中设置一个简单的threadlocal变量,然后想要在self线程中检索其值. 执行后,发现该程序的输出为: null.
该程序的输出与我们的预期有很大不同. 实际上,用InheritableThreadLocal替换ThreadLocal是可以的. 不要太早开心. 为了使用线程池,线程被缓存并重复使用,因为它们缓存线程. 此时,父子线程关系的上下文传递没有任何意义.
因此,线程池InheritableThreadLocal提交,并且获得的值可能在执行上一个任务后留下threadlocal使用实例,这是错误的. 使用仅是在执行任务时传输的常规功能.
上面的问题,transporttable-thread-local项目,已经得到很好的解决,并提供了Java代理支持.
让我们看一下最小集合的原始内容. 首先,让我们看一下ThreadLocal的结构.

ThreadLocal实际上作为键存在于Map中. 该映射是ThreadLocalMap,以私有变量的形式存在于Thread类中. 以上面的图片为例,如果我创建一个ThreadLocal,然后调用set方法,它将首先找到当前线程,然后找到threadLocals,最后将其存储为该映射中的键.
hread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
map.set(this, value);
为了能够完成多线程协调工作,必须提供全套的多线程工具. 包括但不限于:
1. 定义注释和由注释修改的ThreadLocal类


定义一个新的ThreadLocal类,以便在分配值时,可以基于注释对其进行拦截和过滤. 这就要求在定义ThreadLocal时,我们使用提供的ThreadLocal类,而不是jdk提供的两个.
2. 在父线程和子线程之间复制数据
程池中提交任务之前,我们需要一个位置来临时存储父进程的ThreadLocal内容.

由于许多变量是私有的,因此您需要基于反射进行操作. 根据上面提供的ThreadLocal类的结构threadlocal使用实例,我们需要直接在其中操作变量表(这就是jdk不能随意更改变量名的原因).
在临时存储与父线程相关的变量之后,您可以使用活动设置和清理来完成变量的副本.
3. 提供专用的Callable或Runnable
那么这些数据如何组合?仍然依靠我们的任务载体类. 线程池通常通过Callable或Runnable提交线程. 以Runnable为例,让我们看一下这种调用关系.
以下类使用委托模型.

通过这种方式,只要我们在提交任务时使用自定义的Runnable即可;同时,使用自定义ThreadLocal,我们可以正常完成透明传输.

sl4j MDC机制非常好,通常用于保存线程本地的“诊断数据”,然后打印日志组件,其内部实现是基于threadLocal的;但这有一些问题,主线程中的MDC数据集在子线程(多线程池)中不可用,下面将介绍如何解决此问题.
MDC(映射的诊断上下文),它是用于存储诊断日志的线程安全容器. 通常,在处理请求之前,将请求的唯一标识符放在MDC容器中,例如sessionId. 该唯一标签将与日志一起输出. 配置文件可以使用占位符进行变量替换.
类似于上述方法,我们需要提供专用的Callable和Runnable. 另外,为了能够同时支持MDC和普通线程,这两个类使用装饰器模式添加功能. 就单个类而言,外部表示仍然是委托模型.

相同的想法,相同的模式. 区别在于父线程的信息是临时存储的. 我们直接使用MDC的内部方法,并在执行任务之前和之后执行相应的操作.
相同的问题仍然存在于Netflix的保险丝组件Hystrix中. 在Hystrix线程池模式下,需要修改ThreadLocal的透明传输,并且它无法单独完成此功能.
但是,不能仅通过yml文件配置Hystrix策略. 我们指的是在Spring Cloud中扩展此策略,以开发我们自己的策略. 需要继承HystrixConcurrentStrategy.
构造代码仍然很长,您可以检查github项目. 但是有一个地方需要解释.

我们使用装饰器模式逐层嵌套代码,并添加了多线程透明传输功能和MDC传输功能. 这样,我们的类就可以同时在以上三个环境中具有透明的传输功能.
相同的想法可以用于其他组件. 例如,我们在多条调用链文章中提到了在多线程环境中跟踪信息的传输.

通常将数据存储在当前线程中,然后在提交任务时将其包装. 值得注意的是,该方法相对侵入性强,适合于封装在通用的基本工具包中. 如果您在业务中使用此功能,您将被骂死.
那很好吗?
ThreadLocal将导致许多棘手的错误并导致代码污染. 在使用它之前,请确保您确实需要使用它. 例如,如果在SimpleDateFormat类上使用线程局部变量,则可以将其替换为DateTimeFormatter.
我们不擅长解决问题,我们只擅长解决容易出现问题的类.
作者简介: Smell(xjjdog),这是一个公共帐户,不允许程序员绕道而行. 专注于基础架构和Linux. 十年的体系结构和每天数百亿的流量将与您讨论高度并发的世界,并为您带来不同的口味. 我个人的微信xjjdog0,欢迎添加朋友以进行进一步的交流.
最近流行的文章
“必看!java后端,明亮的剑仙”“
相关和流行的后端技术指标
“分析Linux上最常用的命令(选择10年)” CSDN发布的第一天,得到1k的好评. 像是1/8.
“如果这次我不了解Spring Cloud的核心组件,那么我将徒劳地编写这个故事. ”用故事解释核心要素,您会感到满意
“ Linux生产环境中最常用的一套Sed”的“技巧”是Sed最常用的系列文章,通俗易懂. Vim的文章更容易理解.
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-219715-1.html
是可以做到底
但是涉及国家利益的时候
听到你的声音