https://blog.csdn.net/weixin_44637711/article/details/123698016

ThreadLocal父子线程数据传递?

(面试题:如何在子线程拿到父线程threadLocal的值)

1、演示子线程无法拿到值

    /**
     * 直接使用ThreadLocal,无法获得父线程的值
     */
    public static void demo1() {
        ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
        stringThreadLocal.set("今天星期五");
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + stringThreadLocal.get());//Thread-0null
        }).start();
        System.out.println(Thread.currentThread().getName() + "执行完成");
    }

2、使用InheritableThreadLocal解决

    /**
     * 使用InheritableThreadLocal,可以拿到父线程的值,
     * 原理:在线程init方法时传入父线程的threadLocal
     */
    public static void demo2() {
        ThreadLocal<String> stringThreadLocal = new InheritableThreadLocal<>();
        stringThreadLocal.set("今天星期五");
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName()
                               + stringThreadLocal.get());//Thread-0今天星期五
        }).start();
        System.out.println(Thread.currentThread().getName() + "执行完成");
    }

3、带来的问题:在线程池中只能获取初始化线程时父线程的值

 /**
     * 在线程池使用InheritableThreadLocal,因为线程池中的线程是重复利用的,只能拿到线程init时父线程ThreadLocal的值
     *
     * @throws InterruptedException
     */
    public static void demo3() throws InterruptedException {
        InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
        ExecutorService threadPool = Executors.newFixedThreadPool(1);
        threadLocal.set("首次赋值");
        Semaphore semaphore = new Semaphore(1);
        threadPool.submit(() -> {
            System.out.println(Thread.currentThread().getName() + threadLocal.get());
        });
        semaphore.acquire();
        threadLocal.set("再次赋值");
        threadPool.submit(() -> {
            semaphore.release();
            System.out.println(Thread.currentThread().getName()
            + threadLocal.get());//还是初值
        });
        semaphore.acquire();
        threadPool.shutdown();
    }

4、解决:使用阿里开源工具类\\transmittable-thread-local\\

通过装饰类调用前重新将父线程的值进行赋值

 /**
     * 注意:
     * 即使是同一个Runnable任务多次提交到线程池时,
     * 每次提交时都需要通过修饰操作(即TtlRunnable.get(task))以抓取这次提交时的
     * TransmittableThreadLocal上下文的值;
     * 即如果同一个任务下一次提交时不执行修饰而仍然使用上一次的TtlRunnable,
     * 则提交的任务运行时会是之前修饰操作所抓取的上下文。
     *
     * @throws InterruptedException
     */
    public static void demo4() throws InterruptedException {
        TransmittableThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();
        ExecutorService threadPool = Executors.newFixedThreadPool(1);
        threadLocal.set("首次赋值");
        Semaphore semaphore = new Semaphore(1);
        // 额外的处理,生成修饰了的对象ttlRunnable
        Runnable task = TtlRunnable.get(() -> {
            System.out.println(Thread.currentThread().getName() + threadLocal.get());
        });
        threadPool.submit(task);//pool-1-thread-1首次赋值
        semaphore.acquire();
        threadLocal.remove();
        threadLocal.set("再次赋值");
        // 额外的处理,生成修饰了的对象ttlCallable
        Runnable task2 = TtlRunnable.get(() -> {
            System.out.println(Thread.currentThread().getName() + threadLocal.get());
        });
        threadPool.submit(task2);//pool-1-thread-1再次赋值
        semaphore.acquire();
        threadPool.shutdown();
    }

5、解决2:通过线程池包装类解决

   /**
     * 修饰线程池
     * 省去每次Runnable和Callable传入线程池时的修饰,这个逻辑可以在线程池中完成。
     *
     * @throws InterruptedException
     */
    public static void demo5() throws InterruptedException {
        TransmittableThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        ExecutorService threadPool = TtlExecutors.getTtlExecutorService(executorService);
        threadLocal.set("首次赋值");
        Semaphore semaphore = new Semaphore(1);
        threadPool.submit(() -> {
            System.out.println(Thread.currentThread().getName() + threadLocal.get());
        });
        semaphore.acquire();
        threadLocal.set("再次赋值");
        threadPool.submit(() -> {
            semaphore.release();
            System.out.println(Thread.currentThread().getName() + threadLocal.get());//pool-1-thread-1再次赋值
        });
        semaphore.acquire();
        threadPool.shutdown();
    }

附:maven地址

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>transmittable-thread-local</artifactId>
            <version>2.12.6</version>
        </dependency>