利用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); }