TransmittableThreadLocal实现原理

ThreadLocal实现原理

// ThreadLocal.set()源码
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
ThreadLocalMap保存在Thread中:

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

可见threadLocals为Thread的内部变量,每个Thread类的实例,都有自己的threadLocals变量。

// Thread类源码
public class Thread implements Runnable {
  	......
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ......
}

1.1 总结

ThreadLocal属性保存在每个线程实例中,线程实例通过Map结构管理所有的ThreadLocal属性,Map的key为ThreadLocal属性。

1.2 遇到的问题

属性在父子线程中传递的问题:

public class MainToChild implements Runnable {

    public static ThreadLocal<String> threadLocal = new ThreadLocal();

    @Override
    public void run() {
        System.out.println("child : " + Thread.currentThread().getName());
        System.out.println("child : " + threadLocal.get());
    }

    public static void main(String[] args) {

        MainToChild mainToChild = new MainToChild();
        Thread thread = new Thread(mainToChild);
        thread.start();

        threadLocal.set("main");

        System.out.println("main : " + Thread.currentThread().getName());
        System.out.println("main : " + threadLocal.get());
    }
}

ThreadLocal属性是Thread的内部变量,父子线程是不同的线程实例,显然在子线程中无法获取父线程设置的属性:

main : main
child : Thread-0
child : null
main : main

2 InheritableThreadLocal实现原理

public class MainToChild implements Runnable {

    public static InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal();

    @Override
    public void run() {
        System.out.println("child name : " + Thread.currentThread().getName());
        System.out.println("child get main value : " + threadLocal.get());
        threadLocal.set("child");
        System.out.println("child value : " + threadLocal.get());

    }

    public static void main(String[] args) {

        threadLocal.set("main");

        MainToChild mainToChild = new MainToChild();
        Thread thread = new Thread(mainToChild);
        thread.start();

        System.out.println("main name : " + Thread.currentThread().getName());
        System.out.println("main value: " + threadLocal.get());
    }
}

执行结果:

main name : main
child name : Thread-0
child get main value : main
main value: main
child value : child

再看一个:

package com.sankuai.mall.consumerapi.web.controller;

import lombok.SneakyThrows;

/**
 * @author : mazhen
 * Date    : 2021/11/12
 * Time    : 3:11 下午
 * ---------------------------------------
 * Desc    :
 */
public class MainToChild implements Runnable {

    public static InheritableThreadLocal threadLocal = new InheritableThreadLocal();

    @SneakyThrows
    @Override
    public void run() {
        System.out.println("child name : " + Thread.currentThread().getName());
        System.out.println("1 child get main value : " + threadLocal.get());
        System.out.println("2 child get main value : " + threadLocal.get());
    }

    public static void main(String[] args) {

        threadLocal.set("main");

        MainToChild mainToChild = new MainToChild();
        Thread thread = new Thread(mainToChild);
        thread.start();

        threadLocal.set("again main");

        System.out.println("main name : " + Thread.currentThread().getName());
        System.out.println("main value: " + threadLocal.get());
    }

}

执行结果:

child name : Thread-0
main name : main
1 child get main value : main
main value: again main
2 child get main value : main

我们先来看下类的介绍:

This class extends ThreadLocal to provide inheritance of values from parent 
thread to child thread: when a child thread is created, the child receives 
initial values for all inheritable thread-local variables for which the parent 
has values. Normally the child's values will be identical to the parent's; 
however, the child's value can be made an arbitrary function of the parent's 
by overriding the childValue method in this class.
Inheritable thread-local variables are used in preference to ordinary 
thread-local variables when the per-thread-attribute being maintained in 
the variable (e.g., User ID, Transaction ID) must be automatically transmitted
to any child threads that are created.

再看下实现原理:

ThreadLocalMap getMap(Thread t) {
    return t.inheritableThreadLocals;
}

与ThreadLocal一样,InheritableThreadLocal保存在Thread中:

// Thread类源码
public class Thread implements Runnable {
  	......
	ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    ......
}

数据结构也是一样的,我们来看这这个属性有什么特殊逻辑(when a child thread is created, the child receives initial values for all inheritable thread-local variables for which the parent has values),下面代码在Thread类中:

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        ......
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
  		.....
}

static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
}

下面的代码在ThreadLocal中:

private ThreadLocalMap(ThreadLocalMap parentMap) {
       Entry[] parentTable = parentMap.table;
       int len = parentTable.length;
       setThreshold(len);
       table = new Entry[len];

       for (int j = 0; j < len; j++) {
           Entry e = parentTable[j];
           if (e != null) {
               @SuppressWarnings("unchecked")
               ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
               if (key != null) {
                    // 需要注意childValue方法,通过重写这个方法,
                    // 可以实现自定义的拷贝逻辑
                    Object value = key.childValue(e.value);
                    Entry c = new Entry(key, value);
                    int h = key.threadLocalHashCode & (len - 1);
                    while (table[h] != null)
                            h = nextIndex(h, len);
                    table[h] = c;
                    size++;
               }
           }
       }
}

2.1 总结

如果父线程使用了InheritableThreadLocal属性,在子线程初始化的时候,会把父线程InheritableThreadLocal属性中的值,逐个拷贝到子线程的InheritableThreadLocal属性中

2.2 遇到的问题

package com.sankuai.mall.consumerapi.web.controller;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author : mazhen
 * Date    : 2021/11/12
 * Time    : 4:11 下午
 * ---------------------------------------
 * Desc    :
 */
public class ITL {

    public static InheritableThreadLocal<String> param = new InheritableThreadLocal<>();

    public static void main(String[] args) {
        param.set("main");

        ExecutorService pool = Executors.newFixedThreadPool(1);
        pool.submit(() -> {
            System.out.println(param.get());
        });
        param.set("main2");
        pool.submit(() -> {
            System.out.println(param.get());
        });
        pool.submit(() -> {
            System.out.println(param.get());
        });
        pool.shutdown();
    }

}

执行的结果:

main
main
main

InheritableThreadLocal只会在线程池中的线程初次创建的时候,从父线程拷贝属性,而父线程再次修改这个属性时,线程池中的这个线程是无法再次感知到的。

我们在日常的开发中,很少主动new线程,都是使用线程池,而线程池中,不会频繁的创建线程,更多的场景是线程创建一次,重复使用。

再看一个例子:

package com.sankuai.mall.consumerapi.web.controller;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author : mazhen
 * Date    : 2021/11/12
 * Time    : 4:11 下午
 * ---------------------------------------
 * Desc    :
 */
public class ITL {

    public static InheritableThreadLocal<String> param = new InheritableThreadLocal<>();

    public static void main(String[] args) {
        param.set("main");

        ExecutorService pool = Executors.newFixedThreadPool(1);
        pool.submit(() -> {
            System.out.println(param.get());
        });
        pool.submit(() -> {
            param.set("child value only use in this");
            System.out.println(param.get());
        });
        pool.submit(() -> {
            System.out.println(param.get());
        });
        pool.shutdown();
    }

}

执行结果:

main
child value only use in this
child value only use in this

如果线程中的属性在上次使用时被修改,下次使用后,线程中保存的属性值是上次使用时修改的值。

3 TransmittableThreadLocal实现原理

import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author : mazhen
 * Date    : 2021/11/12
 * Time    : 4:11 下午
 * ---------------------------------------
 * Desc    :
 */
public class ITL {

    public static TransmittableThreadLocal<String> param = new TransmittableThreadLocal<>();

    public static void main(String[] args) {
        param.set("main");

        ExecutorService pool = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(1));

        pool.submit(() -> {
            System.out.println(param.get());
        });
        param.set("main2");
        pool.submit(() -> {
            System.out.println(param.get());
        });
        pool.submit(() -> {
            System.out.println(param.get());
        });
        pool.shutdown();
    }

}

执行结果:

main
main2
main2

可以看到,使用TransmittableThreadLocal并配合TtlExecutors.getTtlExecutorService()可以让子线程感知到父线程对共享变量的实时变化。

再看一个:

import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author : mazhen
 * Date    : 2021/11/12
 * Time    : 4:11 下午
 * ---------------------------------------
 * Desc    :
 */
public class ITL {

    public static TransmittableThreadLocal<String> param = new TransmittableThreadLocal<>();

    public static void main(String[] args) {
        param.set("main");

        ExecutorService pool = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(1));

        pool.submit(() -> {
            System.out.println(param.get());
        });
        param.set("main2");
        pool.submit(() -> {
            param.set("child value only use in this");
            System.out.println(param.get());
        });
        pool.submit(() -> {
            System.out.println(param.get());
        });
        pool.shutdown();
    }

}

执行结果:

main
child value only use in this
main2

而且子线程对共享变量的修改不会影响到其他子线程。

我们看下具体的实现原理:

public final void set(T value) {
     // InheritableThreadLocal.set(),这里保留了父类的特性
     super.set(value);
     if (null == value) {
         this.removeValue();
     } else {
         this.addValue();
     }

}



private void addValue() {
    if (!((Map)holder.get()).containsKey(this)) {
       ((Map)holder.get()).put(this, (Object)null);
    }

}

与ThreadLocal保存在Thread中不同,holder就保存在TransmittableThreadLocal中:

private static InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>> holder = new InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>>() {
        protected Map<TransmittableThreadLocal<?>, ?> initialValue() {
            return new WeakHashMap();
        }

        protected Map<TransmittableThreadLocal<?>, ?> childValue(Map<TransmittableThreadLocal<?>, ?> parentValue) {
            return new WeakHashMap(parentValue);
        }
    };

继续往下看就没有其它信息了,我们回过头看TtlExecutors,对普通的ExecutorService进行了封装:

public static ExecutorService getTtlExecutorService(@Nullable ExecutorService executorService) {
        return (ExecutorService)(!TtlAgent.isTtlAgentLoaded() && executorService != null && !(executorService instanceof TtlEnhanced) ? new ExecutorServiceTtlWrapper(executorService) : executorService);
    }

我们看下ExecutorServiceTtlWrapper的实现:

public <T> Future<T> submit(@Nonnull Runnable task, T result) {
    return this.executorService.submit(TtlRunnable.get(task), result);
}


public static TtlRunnable get(@Nullable Runnable runnable) {
    return get(runnable, false, false);
}


public static TtlRunnable get(@Nullable Runnable runnable, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) {
    if (null == runnable) {
         return null;
    } else if (runnable instanceof TtlEnhanced) {
         if (idempotent) {
              return (TtlRunnable)runnable;
         } else {
             throw new IllegalStateException("Already TtlRunnable!");
         }
    } else {
         return new TtlRunnable(runnable, releaseTtlValueReferenceAfterRun);
    }
}

看起来核心就是TtlRunnable了:

public void run() {
     Object captured = this.capturedRef.get();
     if (captured != null && (!this.releaseTtlValueReferenceAfterRun || this.capturedRef.compareAndSet(captured, (Object)null))) {
         
         //线程执行前,备份现场
         Object backup = Transmitter.replay(captured);

         try {
             this.runnable.run();
         } finally {
             // 线程执行完成后,再用拷贝的属性还原现场
          	 // 这样就可以解决InheritableThreadLocal所面临的问题
             Transmitter.restore(backup);
         }

      } else {
          throw new IllegalStateException("TTL value reference is released after run!");
     }
}

我们找备份现场的代码看一下,其实就是把属性复制了一下:

public static Object replay(@Nonnull Object captured) {
      Map<TransmittableThreadLocal<?>, Object> capturedMap = (Map)captured;
      Map<TransmittableThreadLocal<?>, Object> backup = new HashMap();
      Iterator iterator = ((Map)TransmittableThreadLocal.holder.get()).entrySet().iterator();

      // 遍历需要复制的holder中的属性
      while(iterator.hasNext()) {
           Entry<TransmittableThreadLocal<?>, ?> next = (Entry)iterator.next();
           TransmittableThreadLocal<?> threadLocal = (TransmittableThreadLocal)next.getKey();
           // backup
           backup.put(threadLocal, threadLocal.get());
           if (!capturedMap.containsKey(threadLocal)) {
                terator.remove();
                threadLocal.superRemove();
           }
       }

       // clear the TTL values that is not in captured
       // avoid the extra TTL values after replay when run task
       setTtlValuesTo(capturedMap);
       TransmittableThreadLocal.doExecuteCallback(true);
       return backup;
}

看下下面代码的执行结果:

import com.alibaba.ttl.TransmittableThreadLocal;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author : mazhen
 * Date    : 2021/11/12
 * Time    : 6:28 下午
 * ---------------------------------------
 * Desc    :
 */
public class TTL {
    public static TransmittableThreadLocal<String> param =
            new TransmittableThreadLocal<>();

    public static void main(String[] args) {
        param.set("main");
        
        ExecutorService pool = Executors.newFixedThreadPool(1);
        pool.submit(() -> {
            System.out.println(param.get());
        });
        pool.submit(() -> {
            param.set("special");
            System.out.println(param.get());
        });
        param.set("main2");
        pool.submit(() -> {
            System.out.println(param.get());
        });
        pool.shutdown();
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值