其他分享
首页 > 其他分享> > 父线程变量需要传递到子线程使用一种方式InheritableThreadLocal和TransmittableThreadLocal

父线程变量需要传递到子线程使用一种方式InheritableThreadLocal和TransmittableThreadLocal

作者:互联网

前言

ThreadLocal是相对于每一个线程自己使用的本地变量,有这样的一种需求:父线程生成的变量需要传递到子线程中进行使用。需要使用新的InheritableThreadLocal类来实现,但
InheritableThreadLocal的使用利用Thread 的初始化。我们经常在使用线程的时候,都是使用线程池的方式这个时候就需要TransmittableThreadLocal登场来解决。

实践

一、InheritableThreadLocal使用

public class LocalThreadUtil {

    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    private static InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
   

    public static void main(String[] args) {
        threadLocal.set("mian threadLocal");
        inheritableThreadLocal.set("mian inheritableThreadLocal");

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("threadLocal is data is =====" + threadLocal.get());
                System.out.println("inheritableThreadLocal is data is =====" + inheritableThreadLocal.get());
            }
        }).start();
    }
    }

线程输出:

threadLocal is data is =====null
inheritableThreadLocal is data is =====mian inheritableThreadLocal

可以看到inheritableThreadLocal 在子线程中能够获取到父线程的设置的值,我们可以探究一下这个如何做到的。

原理:

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
//这个是在初始化 thread 中的InheritableThreadLocal 成员变量初始化获取中根据父类值
//获取子类的值,返回和父类一摸一样所以有了地址拷贝
    protected T childValue(T parentValue) {
        return parentValue;
    }

//threadLocal 中的 ThreadLocalMap getMap(Thread t) {return t.threadLocals;} 
//是调用线程t.threadLocals; 那说明使用 inheritableThreadLocal 对inheritableThreadLocals 进行初始化操作
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

可以看到InheritableThreadLocal是借助父类的ThreadLocal的能力,继承自ThreadLocal,并且重写了父类的方法:createMap(),getMap(),childValue();Thread 中有两个ThreadLocal和InheritableThreadLocal成员变量,数据类型都是ThreadLocal.ThreadLocalMap 当我们new Thread 新线程调用初始化,操作了成员变量
ThreadLocal 和 InheritableThreadLocal


  ThreadLocal.ThreadLocalMap threadLocals = null;


    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
   public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }


 /**
     * Initializes a Thread with the current AccessControlContext.
     * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean)
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
                      // 最后一个true 需要初始化InheritableThreadLocal 成员变量
        init(g, target, name, stackSize, null, true);
    }


 private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;

        Thread parent = currentThread();
       ...(省略部分)


        g.addUnstarted();

        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
          // inheritThreadLocals  是在初始化传入的,表示需要初始化inheritableThreadLocals 
          //parent.inheritableThreadLocals != null 初始化线程中有主线程有InheritableThreadLocal这个变量
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        //这个是对copy 主线程中parent.inheritableThreadLocals变量
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

ThreadLocal.createInheritedMap中调用 具体实现方法,对主线程中ThreadLocalMap 进行数据拷贝

 private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                    //这个也是InheritableThreadLocal 重写子线程,直接返回父线程的值
                    //也可protected T childValue(T parentValue) {return parentValue  } 中看出这里浅拷贝
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

从上面的分析可以知道我们使用在InheritableThreadLocal父传子的拷贝过程中非常依赖
new Thread 这个方法,因为这个调用初始化方法对子线程的init方法对thread 中InheritableThreadLocal的进行初始化操作。我们可以验证一下,但线程不进行初始化是否这个还有用。

public class LocalThreadUtil {

    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    private static InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
    private static TransmittableThreadLocal<String> transmittableThreadLocal = new TransmittableThreadLocal<>();

    private static ExecutorService executorService = Executors.newSingleThreadExecutor();


    public static void main(String[] args) throws InterruptedException {

        inheritableThreadLocal.set("old");
        CountDownLatch countDownLatch = new CountDownLatch(1);

        executorService.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("first children =====" + inheritableThreadLocal.get());
                countDownLatch.countDown();
            }
        });
        countDownLatch.await();

// 改变inheritableThreadLocal 的值
        inheritableThreadLocal.set("update");

        CountDownLatch countDownLatch2 = new CountDownLatch(1);
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("second children =====" + inheritableThreadLocal.get());
                countDownLatch2.countDown();
            }
        });
        countDownLatch2.await();
        System.out.println("main  is ==="+inheritableThreadLocal.get());
    }
}

我这里使用newSingleThreadExecutor 只有一个线程,那么在第二次改变inheritableThreadLocal 值再去看执行一个线程的时候就会采用刚刚已经初始化好的线程。结果也证明当线程不再初始化的时候,获取到的值还是线程第一次出化好的值,

first children =====old
second children =====old
main  is ===update

一、TransmittableThreadLocal使用

maven 依赖

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

代码的处理

public class LocalThreadUtil {

    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    private static InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
    private static TransmittableThreadLocal<String> transmittableThreadLocal = new TransmittableThreadLocal<>();

    private static ExecutorService executorService = Executors.newSingleThreadExecutor();



    public static void main(String[] args) throws InterruptedException {

        transmittableThreadLocal.set("old");
        CountDownLatch countDownLatch = new CountDownLatch(1);

//这里执行的 是TtlRunnable 
        executorService.execute(TtlRunnable.get(new Runnable() {
            @Override
            public void run() {
                System.out.println("first children =====" + transmittableThreadLocal.get());
                countDownLatch.countDown();
            }
        }));
        countDownLatch.await();

        transmittableThreadLocal.set("update");

        CountDownLatch countDownLatch2 = new CountDownLatch(1);
        executorService.execute(TtlRunnable.get(new Runnable() {
            @Override
            public void run() {
                System.out.println("second children =====" + transmittableThreadLocal.get());
                countDownLatch2.countDown();
            }
        }));
        countDownLatch2.await();
        System.out.println("main  is ==="+transmittableThreadLocal.get());
    }
}

打印结果:可以考到当主线程中设置的值改变时候,子线程也同样发生了改变。需要注意的是TransmittableThreadLocal 的使用需要TtlRunnable 的配合使用,才能达到使用效果。

first children =====old
second children =====update
main  is ===update

标签:线程,到子,inheritableThreadLocal,ThreadLocal,InheritableThreadLocal,TransmittableThr
来源: https://blog.csdn.net/fajing_feiyue/article/details/113785780