一、示例

线程池内的线程并没有父子关系,所以不适合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);
}
}

参考:

transmittableThreadLocal

ThreadLocal (三):为何TransmittableThreadLocal的更多相关文章

  1. ThreadLocal系列(三)-TransmittableThreadLocal的使用及原理解析

    ThreadLocal系列(三)-TransmittableThreadLocal的使用及原理解析 上一篇:ThreadLocal系列(二)-InheritableThreadLocal的使用及原理解 ...

  2. ThreadLocal的进化——TransmittableThreadLocal

    上一篇文章中,我们谈到了 InheritableThreadLocal,它解决了 ThreadLocal 针对父子线程无法共享上下文的问题.但我们可能听说过阿里的开源产品TransmittableTh ...

  3. ThreadLocal系列(二)-InheritableThreadLocal的使用及原理解析

    ThreadLocal系列之InheritableThreadLocal的使用及原理解析(源码基于java8) 上一篇:ThreadLocal系列(一)-ThreadLocal的使用及原理解析 下一篇 ...

  4. ThreadLocal的几种误区

    最近由于需要用到ThreadLocal,在网上搜索了一些相关资料,发现对ThreadLocal经常会有下面几种误解 一.ThreadLocal是java线程的一个实现       ThreadLoca ...

  5. 【原理】Java的ThreadLocal实现原理浅读

    当前线程的值传递,ThreadLocal 通过ThreadLocal设值,在线程内可获取,即时获取值时在其它Class或其它Method. public class BasicUsage { priv ...

  6. ThreadLocal的使用场景分析

    目录 一.ThreadLocal介绍 二.使用场景1——数据库事务问题 2.1 问题背景 2.2 方案1-修改接口传参 2.3 方案2-使用ThreadLocal 三.使用场景2——日志追踪问题 四. ...

  7. 多线程程序设计学习(12)Thread-soecific storage pattern

    Thread-Specific-Storage[线程保管箱] 一:Thread-Specific Storage的参与者--->记录日志的线程(ClientThread)--->负责获取不 ...

  8. 多线程学习三:Thread API,ThreadLocal,synchronized,volatile和Condition

    一.Thread API: setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) 首先要了解什么是Thread. ...

  9. 使用 transmittable-thread-local 组件解决 ThreadLocal 父子线程数据传递问题

    在某个项目中,需要使用mybatis-plus多租户功能以便数据隔离,前端将租户id传到后端,后端通过拦截器将该租户id设置到ThreadLocal以便后续使用,代码大体上如下所示: ThreadLo ...

随机推荐

  1. Retrofit2和RxJava配合使用Demo

    和Retrofit2单独使用有一些区别 首先,同样写个interface,这里是GitHubService.java,我们这次要实现获取官方提供的测试接口的数据 public interface Gi ...

  2. Atitit.创建快捷方式 windows快捷方式的原理

    Atitit.创建快捷方式 windows快捷方式的原理 1. Windows中有2种快捷方式的文件:1 2. Jshortcut2 2.1. 提示新不上jshortcut.dll2 2.2. 使用w ...

  3. 转:NHibernate 存储过程

    http://stackoverflow.com/questions/928847/how-to-get-the-return-value-from-a-sql-server-stored-proce ...

  4. C++语言基础(12)-虚函数

    一.虚函数使用的注意事项 1.只需要在虚函数的声明处加上 virtual 关键字,函数定义处可以加也可以不加. 2.为了方便,你可以只将基类中的函数声明为虚函数,这样所有子类中具有遮蔽(覆盖)关系的同 ...

  5. cf #363 d

      time limit per test 2 seconds memory limit per test 256 megabytes input standard input output stan ...

  6. vs2015创建webService

  7. php 远程调用redis

    <?php $redis_conf = array ( "active_code"=>array( "host" => "14.29 ...

  8. 非常好用的两个PHP函数 serialize()和unserialize()

    使用serialize()函数和unserialize()函数,这两个函数的用法真是绝配,一个是进行序列化存储,另一个则是进行序列化恢复,方便极了 今天,在做一个关于PING服务的东西,由于不想把pi ...

  9. Unity3D学习笔记——NGUI之UIScrollView

    前言:有的时候Panel会被截取,里面的内容就不能显示完整,所以需要为其添加 scroll view组件. 一:将Panel放进一个scroll view只需要简单的3步: 1.首先在UI Root下 ...

  10. Java 基础巩固,根深而叶茂

    #J2SE ##基础 八种基本数据类型的大小,以及他们的封装类. 八种基本数据类型,int ,double ,long ,float, short,byte,character,boolean 对应的 ...