InheritableThreadLocal、TransmittableThreadLocal三者区别
ThreadLocal 解决的是每个线程可以拥有自己线程的变量实例。可以从隔离的角度解决变量线程安全的问题。ThreadLocal的缺点:它不支持子线程。因为map是绑定在currentThread中的。子线程和父线程并不是一个Thread。
InheritableThreadLocal就是支持子线程的ThreadLocal。
public class UserContext { private static InheritableThreadLocal<User> userContext = new InheritableThreadLocal<>(); public static User getUser() { return userContext.get(); } public static void setUser(User user) { userContext.set(user); } public static void removeUser() { userContext.remove(); } }
public class InheritableThreadLocalTest { public static void main(String[] args) { User user = new User(); user.setUsername("jim"); UserContext.setUser(user); System.out.println(user + "---->" + user.getUsername()); new Thread(() -> { User user1 = UserContext.getUser(); System.out.println(user1 + "++++>" + user1.getUsername()); }).start(); } } User{username='jim'}---->jim User{username='jim'}++++>jim
InheritableThreadLocal主要的代码是在 Thread 的 init() 方法中
if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
inheritableThreadLocals 还是一个 ThreadLocalMap,只不过是在 Thread 的 init 方法中把父Thread的inheritableThreadLocals 变量 copy 了一份给自己。同样借助 ThreadLocalMap 子线程可以获取到父线程的所有变量。缺点就是 Thread的 init 方法是在线程构造方法中 copy的,也就是在线程复用的线程池中是没有办法使用的。
TransmittableThreadLocal 简称(TTL)是阿里开源的一款支持线程池的 ThreadLocal 组件
/** * 线程池失效 */ public static void inheritableThreadLocalTest1() { ExecutorService executorService = newFixedThreadPool(1); User user = new User(); user.setUsername("jim"); UserContext.setUser(user); executorService.submit(() -> { User user1 = UserContext.getUser(); System.out.println(user1 + "--->" + user1.getUsername()); }); User jack = new User(); jack.setUsername("jack"); UserContext.setUser(jack); executorService.submit(() -> { User user1 = UserContext.getUser(); System.out.println(user1 + "++++>" + user1.getUsername()); }); } User{username='jim'}--->jim User{username='jim'}++++>jim
改成阿里的TTL
public class UserContext { private static TransmittableThreadLocal<User> userContext = new TransmittableThreadLocal<>(); public static User getUser() { return userContext.get(); } public static void setUser(User user) { userContext.set(user); } public static void removeUser() { userContext.remove(); } }
public static void inheritableThreadLocalTest1() { ExecutorService executorService = TtlExecutors.getTtlExecutorService(newFixedThreadPool(1)); User user = new User(); user.setUsername("jim"); UserContext.setUser(user); executorService.submit(() -> { User user1 = UserContext.getUser(); System.out.println(user1 + "--->" + user1.getUsername()); }); User jack = new User(); jack.setUsername("jack"); UserContext.setUser(jack); executorService.submit(() -> { User user1 = UserContext.getUser(); System.out.println(user1 + "++++>" + user1.getUsername()); }); }
User{username='jim'}--->jim
User{username='jack'}++++>jack
TTL 的做法也比较直接,使用了装饰器模式,既然 InheritableThreadLocal 只是在线程Create 的时候复制一份父线程数据,那么为了支持线程池就需要在 Thread 的 run 方法之前把父线程的数据 copy 一下就可以了。从源码中看是
public static ExecutorService getTtlExecutorService(ExecutorService executorService) { if (executorService == null || executorService instanceof ExecutorServiceTtlWrapper) { return executorService; } return new ExecutorServiceTtlWrapper(executorService); }
ExecutorServiceTtlWrapper包装普通的 ExecutorService,然后填充submit 方法
@Override public <T> Future<T> submit(Runnable task, T result) { return executorService.submit(TtlRunnable.get(task), result); }
TtlRunnable 类实现了Runable 方法,并且重写run 方法。就是在run方法之前复制父线程的ThreadLocal变量。当线程执行时,调用 TtlRunnable run 方法,TtlRunnable 会从 AtomicReference 中获取出调用线程中所有的上下文,并把上下文给 TransmittableThreadLocal.Transmitter.replay 方法把上下文复制到当前线程。并把上下文备份。当线程执行完,调用 TransmittableThreadLocal.Transmitter.restore 并把备份的上下文传入,恢复备份的上下文,把后面新增的上下文删除,并重新把上下文复制到当前线程
浙公网安备 33010602011771号