Administrator
发布于 2024-08-15 / 22 阅读
0
0

InheritableThreadLocal 用法和弊端

  1. 利用InheritableThreadLocal进行父子线程传递

    设置流程:Thread中 构造函数中会检查parent.inheritableThreadLocals中是否存在,然后用ThreadLocal.createInheritedMap()方法创建新的ThreadLocalMap,这个map保留父线程的ThreadLocal里面的值,同时把这个map复制给his.inheritableThreadLocals;

    获取流程:由于InheritableThreadLocal集成值ThreadLocal并重写了getMap方法

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

    所以当ThreadLocal获取的时候,就会在.inheritableThreadLocal中获取设置的值。

    private T get(Thread t) {
           ThreadLocalMap map = getMap(t);
           if (map != null) {
               ThreadLocalMap.Entry e = map.getEntry(this);
               if (e != null) {
                   @SuppressWarnings("unchecked")
                   T result = (T) e.value;
                   return result;
               }
           }
           return setInitialValue(t);
       }

    这样就可以完成了父子线程的值传递

    这样会带来什么问题,我们看如下的操作

    InheritableThreadLocal<Integer> local = new InheritableThreadLocal();
           local.set(123);
           new Thread(()->{
               while(true){
                   System.out.println(Thread.currentThread()+" local"+local.get() );
                   try {
                       Thread.sleep(1000l);
                   }
                   catch (InterruptedException e) {
                       throw new RuntimeException(e);
                   }
               }
           }).start();
           for(int i = 0 ; i < 10 ; i++){
               local.set(i);
               System.out.println("change local value : "+local.get() );
               try {
                   Thread.sleep(1000l);
               }
               catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }

    change local value0 Thread[#21,Thread-0,5,main] local123 change local value1 Thread[#21,Thread-0,5,main] local123 Thread[#21,Thread-0,5,main] local123 change local value2 Thread[#21,Thread-0,5,main] local123 change local value3 Thread[#21,Thread-0,5,main] local123 change local value4 change local value5 Thread[#21,Thread-0,5,main] local123 change local value6 Thread[#21,Thread-0,5,main] local123 Thread[#21,Thread-0,5,main] local123 change local value7 change local value8 Thread[#21,Thread-0,5,main] local123 change local value9 Thread[#21,Thread-0,5,main] local123 Thread[#21,Thread-0,5,main] local123 Thread[#21,Thread-0,5,main] local123

    当线程启动之后,如果在进行对value的引用修改是不起作用的。原因是因为构造的时候

    已经对这个inheritableThreadLocals完成了赋值。

    那如果要是不修改引用传递那,比如修改value内部的值:

    答案是可以的,因为复制给inheritableThreadLocals时候是引用传递 ,不是复制新的对象。

    这样在线程池中运用的时候,就会存在问题,原因是为了降低开启和销毁线程我们使用线程池,查询线程池源码中 runWorker(Worker w) 方法是while循环执行worker中run方法中的内容。如果不创建新的线程,这样ThreadLocal的set方法就不起作用了。

    阿里开源的TransmittableThreadLocal可以结局该问题:

    实现原理,通过Transmittee接口:捕获、重发、恢复动作完成对.inheritableThreadLocal的赋值。

    public interface Transmittee<C, B> {
               C capture();
               B replay(@NonNull C captured);
               B clear();
               void restore(@NonNull B backup);
           }

    例如:TTLRunnable中源码如下

    public void run() {
           final Object captured = capturedRef.get();
           if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) {
               throw new IllegalStateException("TTL value reference is released after run!");
           }
    
           final Object backup = replay(captured);
           try {
               runnable.run();
           } finally {
               restore(backup);
           }
       }

    例子:

    AtomicInteger integer = new AtomicInteger();
           integer.set(0);
           TransmittableThreadLocal<Integer> contextHolder = new TransmittableThreadLocal<>();
           contextHolder.set(0);
           ThreadLocal<Integer> local = new InheritableThreadLocal();
           AtomicInteger integer1 = new AtomicInteger();
           integer1.set(0);
           local.set(0);
    
           ExecutorService executorService = Executors.newFixedThreadPool(1);
           for (int i = 0; i < 10; i++) {
               local.set(i);
               contextHolder.set(i);
    //            local.get().incrementAndGet();
    //            contextHolder.get().incrementAndGet();
               executorService.execute(TtlRunnable.get(()->{
    
                   System.out.println(Thread.currentThread()+" contextHolder "+contextHolder.get() );
    //                System.out.println(Thread.currentThread()+" contextHolder "+contextHolder.get().incrementAndGet());
                   System.out.println(Thread.currentThread()+" local "+local.get() );
    //                System.out.println(Thread.currentThread()+" local "+local.get().incrementAndGet());
               }));
               try {
                   Thread.sleep(1000l);
               }
               catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
           for (int i = 0; i < 10; i++) {
               local.set(i);
               contextHolder.set(i);
    //            local.get().incrementAndGet();
    //            contextHolder.get().incrementAndGet();
               executorService.execute(TtlRunnable.get(()->{
    
                   System.out.println(Thread.currentThread()+" contextHolder "+contextHolder.get() );
    //                System.out.println(Thread.currentThread()+" contextHolder "+contextHolder.get().incrementAndGet());
                   System.out.println(Thread.currentThread()+" local "+local.get() );
    //                System.out.println(Thread.currentThread()+" local "+local.get().incrementAndGet());
               }));
               try {
                   Thread.sleep(1000l);
               }
               catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }

    但是这里也会有问题。

    看如下代码 这样每次当线程执行完,下次重新设置值的时候就是想要的值,而不是拿着map一直跑

    Map<Integer, Integer> map = new HashMap<>();
            map.put(1, 1);
            TransmittableThreadLocal<Map<Integer, Integer>> contextHolder = new TransmittableThreadLocal() {
                @Override
                protected Map<Integer, Integer> initialValue() {
                    return new HashMap<>();
                }
    
                @Override
                protected Map<Integer, Integer> childValue(Object parentValue) {
                    return new HashMap<>((Map) parentValue);
                }
    
                @Override
                public Map<Integer, Integer> copy(Object parentValue) {
                    return new HashMap<>((Map) parentValue);
                }
            };
    
            ExecutorService executorService = Executors.newFixedThreadPool(1);
            for (int i = 0; i < 10; i++) {
                contextHolder.set(map);
                executorService.execute(TtlRunnable.get(() -> {
                    System.out.println(Thread.currentThread() + " contextHolder " + contextHolder.get());
                }));
                contextHolder.remove();
                map.put(++i, i);
            }


评论