ThreadLocal系列使用问题及解决方案

1、ThreadLocal使用及问题

代码

public static void threadLocal(){
         ThreadLocal threadLocal = new ThreadLocal();
         threadLocal.set(4);
        System.out.println("parent - " +threadLocal.get());
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("child -" +threadLocal.get());
            }
        }).start();
    }

运行结果

parent - 4
child -null

可以看出当在主线程中开启一个行程时是获取不到在主线程中set的值的,InheritableThreadLocal解决了这个问题

InheritableThreadLocal使用及问题

代码

    public static void inherThreadLocal(){
        ThreadLocal threadLocal = new InheritableThreadLocal();
        threadLocal.set(4);
        System.out.println("parent - " +threadLocal.get());
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("child -" + threadLocal.get());
            }
        }).start();
    }

运行结果

parent - 4
child -4

为什么可以获取呢。我们看一下Thread的代码中
在这里插入图片描述
获取父线程,其中inheritableThreadLocals属性是非null的,接着往下的看

        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

核心在这,会将父线程的inheritableThreadLocals传递给当前子线程。createInheritedMap方法会重新n创建一个map,这就会有一个问题如果是使用线程池时。可能不在重新创建新的线程。那这时候是不是就传递不了了。
代码如下

 public static void withThreadPool(){
        ThreadLocal threadLocal = new InheritableThreadLocal();
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        threadLocal.set(1);
        //这时线程池会进行初始化线程
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("child1 - " +threadLocal.get());
            }
        });

        executorService.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("child2 - " + threadLocal.get());
            }
        });
        executorService.shutdown();
    }

结果

child1 - 1
child2 - 1

好像没什么问题。猜想错了吗?我们换个写法代码如下

public static void withThreadPool(){
        ThreadLocal threadLocal = new InheritableThreadLocal();
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        //这时线程池会进行初始化线程
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("child1 - " +threadLocal.get());
            }
        });
        threadLocal.set(1);
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("child2 - " + threadLocal.get());
            }
        });
        executorService.shutdown();
    }

结果

child1 - null
child2 - null

获取不到了,两块的代码有什么不同呢。仔细看下。threadLocal.set(1);放在了excute之后,那这是为什么呢。看源码execute方法
在这里插入图片描述当工作线程数小于核心线程数时会调用addWorker方法,接下来我们看该方法,其中有

 w = new Worker(firstTask);
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

可以看到是进行了new Thread 操作,上面我们知道在创建线程的时候会将父线程的map传递给子线程。
那我们将线程池的数量大小设置成2个时,
结果

child1 - null
child2 - 1

所以我们可以知道了问题所在 InheritableThreadLocal 在线程池的时候会有问题
解决方案: 阿里的transmittable-thread-local

TransmittableThreadLocal使用

代码

public class Test {

    static ThreadLocal<String> parent = new TransmittableThreadLocal<>();
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        ExecutorService ttlExecutorService = TtlExecutors.getTtlExecutorService(executorService);
        Runnable task = new Task();

        ttlExecutorService.submit(task);
        TimeUnit.SECONDS.sleep(1);

        parent.set("parent value");
        ttlExecutorService.submit(task);
        TimeUnit.SECONDS.sleep(1);

        String value = parent.get();

        System.out.println("out value: " + value);
        ttlExecutorService.shutdown();
    }

    public static class Task implements Runnable {
        @Override
        public void run() {
            System.out.println("=======");
            String value1 = parent.get();
            System.out.println("in value1: " + value1);
            parent.set("child value");
            String value2 = parent.get();
            System.out.println("in value2 " + value2);
        }
    }

}

结果

=======
in value1: null
in value2 child value
=======
in value1: parent value
in value2 child value
out value: parent value

可以看到可以正常传递。
在这我只能仰望阿里的大佬

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值