ThreadLocal (三):为何TransmittableThreadLocal
一、示例
线程池内的线程并没有父子关系,所以不适合InheritableThreadLocal的使用场景
public class ThreadPoolInheritableThreadLocalDemo {
// static ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
// static ExecutorService pool = Executors.newFixedThreadPool(2);
static TransmittableThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();
static ExecutorService pool = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(3));
public static void main(String[] args) {
for(int i=0;i<100;i++) {
int j = i;
pool.execute(new Thread(new Runnable() {
@Override
public void run() {
ThreadPoolInheritableThreadLocalDemo.threadLocal.set("superWorld"+j);
ThreadPoolInheritableThreadLocalDemo.pool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +
" : " +
ThreadPoolInheritableThreadLocalDemo.threadLocal.get());
}
});
}
}));
}
}
}
二、TransmittableThreadLocal实现分析

读取线程间传递的ThreadLocal 值比较麻烦,ThreadLocal 和 InheritableThreadLocal 都没有开放内部的 ThreadLocalMap,不能直接读取。
所以要么自己完全实现一套 ThreadLocalMap 机制(如 Netty 的 FastThreadLocal),要么就是自己实现 ThreadLocal 的子类,在每次调用 ThreadLocal
的 set/get/remove 等接口的时候,为 Thread 记录到底绑定了哪些需要发生线程间传递的 ThreadLocal 对象。
/**
*实际存储值的工作还是父类ThreadLocal完成
*TransmittableThreadLocal 只是记录了哪些线程使用了TransmittableThreadLocal对象
*/
@Override
public final void set(T value) {
super.set(value);
if (null == value) { // may set null to remove value
removeValue();
} else {
addValue();
}
}/**
*holder 只是为了记录使用了哪些 TransmittableThreadLocal 对象
*在构造TtlRunnable/TtlCallable 的时候, 通过holder取得对应的TransmittableThreadLocal
*InheritableThreadLocal的默认值是WeakHashMap
*/
private static InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>> holder =
new InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>>() {
@Override
protected Map<TransmittableThreadLocal<?>, ?> initialValue() {
return new WeakHashMap<TransmittableThreadLocal<?>, Object>();
} @Override
protected Map<TransmittableThreadLocal<?>, ?> childValue(Map<TransmittableThreadLocal<?>, ?> parentValue) {
return new WeakHashMap<TransmittableThreadLocal<?>, Object>(parentValue);
}
}; private void addValue() {
if (!holder.get().containsKey(this)) {
holder.get().put(this, null); // WeakHashMap supports null value.
}
} private void removeValue() {
holder.get().remove(this);
}
调用ThreadPoolInheritableThreadLocalDemo.threadLocal.set("superWorld"+j)时,
holder.get().containskey(this) 为false

2. TtlRunnable
构造TtlRunable时,设置线程对应的Map<TransmittableThreadLocal<?>, Object>>
private TtlRunnable(Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
//
this.copiedRef = new AtomicReference<Map<TransmittableThreadLocal<?>, Object>>(TransmittableThreadLocal.copy());
this.runnable = runnable;
this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
}
TransmittableThreadLocal.copy
static Map<TransmittableThreadLocal<?>, Object> copy() {
Map<TransmittableThreadLocal<?>, Object> copy = new HashMap<TransmittableThreadLocal<?>, Object>();
for (TransmittableThreadLocal<?> threadLocal : holder.get().keySet()) {
copy.put(threadLocal, threadLocal.copyValue());
}
return copy;
}

3.运行时,备份和恢复Map<TransmittableThreadLocal<?>, Object>
TtlRunnable#run
@Override
public void run() {
Map<TransmittableThreadLocal<?>, Object> copied = copiedRef.get();
if (copied == null || releaseTtlValueReferenceAfterRun && !copiedRef.compareAndSet(copied, null)) {
throw new IllegalStateException("TTL value reference is released after run!");
} Map<TransmittableThreadLocal<?>, Object> backup = TransmittableThreadLocal.backupAndSetToCopied(copied);
try {
runnable.run();
} finally {
TransmittableThreadLocal.restoreBackup(backup);
}
}

参考:
ThreadLocal (三):为何TransmittableThreadLocal的更多相关文章
- ThreadLocal系列(三)-TransmittableThreadLocal的使用及原理解析
ThreadLocal系列(三)-TransmittableThreadLocal的使用及原理解析 上一篇:ThreadLocal系列(二)-InheritableThreadLocal的使用及原理解 ...
- ThreadLocal的进化——TransmittableThreadLocal
上一篇文章中,我们谈到了 InheritableThreadLocal,它解决了 ThreadLocal 针对父子线程无法共享上下文的问题.但我们可能听说过阿里的开源产品TransmittableTh ...
- ThreadLocal系列(二)-InheritableThreadLocal的使用及原理解析
ThreadLocal系列之InheritableThreadLocal的使用及原理解析(源码基于java8) 上一篇:ThreadLocal系列(一)-ThreadLocal的使用及原理解析 下一篇 ...
- ThreadLocal的几种误区
最近由于需要用到ThreadLocal,在网上搜索了一些相关资料,发现对ThreadLocal经常会有下面几种误解 一.ThreadLocal是java线程的一个实现 ThreadLoca ...
- 【原理】Java的ThreadLocal实现原理浅读
当前线程的值传递,ThreadLocal 通过ThreadLocal设值,在线程内可获取,即时获取值时在其它Class或其它Method. public class BasicUsage { priv ...
- ThreadLocal的使用场景分析
目录 一.ThreadLocal介绍 二.使用场景1——数据库事务问题 2.1 问题背景 2.2 方案1-修改接口传参 2.3 方案2-使用ThreadLocal 三.使用场景2——日志追踪问题 四. ...
- 多线程程序设计学习(12)Thread-soecific storage pattern
Thread-Specific-Storage[线程保管箱] 一:Thread-Specific Storage的参与者--->记录日志的线程(ClientThread)--->负责获取不 ...
- 多线程学习三:Thread API,ThreadLocal,synchronized,volatile和Condition
一.Thread API: setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) 首先要了解什么是Thread. ...
- 使用 transmittable-thread-local 组件解决 ThreadLocal 父子线程数据传递问题
在某个项目中,需要使用mybatis-plus多租户功能以便数据隔离,前端将租户id传到后端,后端通过拦截器将该租户id设置到ThreadLocal以便后续使用,代码大体上如下所示: ThreadLo ...
随机推荐
- SQL Server事务详解
事务定义: 事务是单个的工作单元.如果某一事务成功,则在该事务中进行的所有数据更改均会提交,成为数据库中的永久组成部分.如果事务遇到错误且必须取消或回滚,则所有数据更改均被清除. 事务三种运行模式: ...
- chrome 一进入调试页面就会自己主动打断点
近期在用chrome调试页面时.一刷新页面总会自己主动打断点.自己鼓捣了一下,发现 把它改为这个样子的时候就能够解决问题,即把调试面板上的第5个button从Deactivate breakpoint ...
- MySQL 慢查询日志(Slow Query Log)
同大多数关系型数据库一样.日志文件是MySQL数据库的重要组成部分.MySQL有几种不同的日志文件.通常包含错误日志文件,二进制日志,通用日志.慢查询日志.等等.这些日志能够帮助我们定位mysqld内 ...
- Atitit.upnp SSDP 查找nas的原理与实现java php c#.net c++
Atitit.upnp SSDP 查找nas的原理与实现java php c#.net c++ 1. 查找nas的原理1 2. 与dlna的关系1 3. 与ssdp的关系1 4. Cling - Ja ...
- C#实现插件的“动态替换”
如果某个"功能"需要动态更新?这种动态更新,可能是需求驱动的,也可能是为了修改 BUG,面对这种场景,如何实现“热插拔”呢?先解释一下“热插拔”:在系统运行过程动态替换某些功能,不 ...
- angularjs 可以加入html标签方法------ng-bind-html的用法总结(1)
本篇主要讲解angular中的$sanitize这个服务.此服务依赖于ngSanitize模块.(这个模块需要加载angular-sanitize.js插件) 要学习这个服务,先要了解另一个指令: n ...
- cp实现无提示覆盖拷贝
在我们使用cp批量拷贝时,即使使用cp -rf ,遇到需要覆盖时,也是需要确认是否覆盖的. 解决办法1: 我们使用alias看一下别名设置会发现这样一行 alias cp='cp -i' 意思就是使用 ...
- input type="file"文件上传时得到文件的本地路劲
<!DOCTYPE html><html><head> <meta charset="UTF-8"> <meta name=& ...
- C语言 结构体(嵌套结构体--结构体数组)
//结构体--嵌套结构体和结构体数组 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> ...
- linux系统启动过程具体解释-开机加电后发生了什么 --linux内核剖析(零)
本文參考了例如以下文章 深入理解linux启动过程 mbr (主引导记录(Master Boot Record)) 电脑从开机加电到操作系统main函数之前执行的过程 详细解释linux系统的启动过程 ...