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 并把备份的上下文传入,恢复备份的上下文,把后面新增的上下文删除,并重新把上下文复制到当前线程

  

 

 

      

posted on 2023-02-11 12:49  溪水静幽  阅读(521)  评论(0)    收藏  举报