美文网首页java多线程java
java线程间ThreadLocal的传递

java线程间ThreadLocal的传递

作者: 水煮鱼又失败了 | 来源:发表于2021-02-23 22:21 被阅读0次

    1 场景

    本文主要讲解java线程间ThreadLocal的传递

    适用如下场景:

    (1)线程内new的新的线程,继承父线程的ThreadLocal

    (2)线程内调用线程池的线程,继承调用线程的ThreadLocall

    2 需确认问题

    2.1 继承线程的ThreadLocal的含义

    新创建的线程,拥有原线程内部的所有的ThreadLocal的配置,和手动配置一遍一样。

    2.2 子线程内的ThreadLocal的值和父线程内的有什么关系?

    子线程内被传递的ThreadLocal的和父线程内的ThreadLocal是一样的。

    在此需注意,java中的参数传递值传递,对于对象传递的值对象的引用的值

    因此ThreadLocal中定义的泛型如果不是String基本类型包装类型等特殊类型,子线程中更改了ThreadLocal内的值,父线程中一起变更,因为两个ThreadLocal中的存储的引用地址一样,对应的是同一个对象,同理,父线程内的ThreadLocal中的值变更后,子线程内的值也会变更。。

    2.3 父线程内的ThreadLocal清除后,子线程是否受影响?

    不受影响。

    父线程的ThreadLocal的底层的ThreadMap复制了一份到子线程。父线程、子线程,均需要remove掉ThreadLocal。

    3 ThreadLocal的使用

    3.1 定义

    /**
     * 普通变量线程
     */
    public class NormalThread implements Runnable {
        
        /**
         * 普通-线程变量
         */
        private static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<>();
        
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            
            // (1)设置线程变量内容
            String value = threadName + "内容";
            THREAD_LOCAL.set(value);
            // (2)输出线程变量内容
            System.out.println("[当前线程]:【" + threadName + "】,[线程变量]:【" + THREAD_LOCAL.get() + "】,匹配结果:" + (value.equals(THREAD_LOCAL.get())));
            // (3)清空线程变量内容
            THREAD_LOCAL.remove();
        }
        
    }
    

    3.2 调用

    System.out.println("----------【1、普通变量线程】----------");
    for (int i = 0; i < 3; i++) {
        new Thread(new NormalThread(), "Thread" + i).start();
    }
    

    3.3 输出

    ----------【1、普通变量线程】----------
    [当前线程]:【Thread0】,[线程变量]:【Thread0内容】,匹配结果:true
    [当前线程]:【Thread1】,[线程变量]:【Thread1内容】,匹配结果:true
    [当前线程]:【Thread2】,[线程变量]:【Thread2内容】,匹配结果:true
    

    4 手动new的线程继承

    使用JDK自带的InheritableThreadLocal实现此功能。

    4.1 定义

    /**
     * 继承变量线程
     */
    public class InheritableThread implements Runnable {
        
        /**
         * 继承线程传递-线程变量
         */
        private static final ThreadLocal<String> INHERIABLE_THREAD_LOCAL = new InheritableThreadLocal<>();
        
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            
            // (1)设置线程变量内容
            String value = threadName + "内容";
            INHERIABLE_THREAD_LOCAL.set(value);
            
            // (2)执行子线程
            new Thread(() -> {
                String childThreadName = Thread.currentThread().getName();
                System.out.println("[当前线程-子线程]:【" + childThreadName + "】,[线程变量]:【" + INHERIABLE_THREAD_LOCAL.get() + "】,匹配结果:" + (value.equals(INHERIABLE_THREAD_LOCAL.get())));
                INHERIABLE_THREAD_LOCAL.remove();
            }, threadName + "-child").start();
            
            // 让子线程先执行(测试使用)
            try {
                TimeUnit.MICROSECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            // (3)输出线程变量内容
            System.out.println("[当前线程-父线程]:【" + threadName + "】,[线程变量]:【" + INHERIABLE_THREAD_LOCAL.get() + "】,匹配结果:" + (value.equals(INHERIABLE_THREAD_LOCAL.get())));
            
            // (4)清空线程变量内容
            INHERIABLE_THREAD_LOCAL.remove();
        }
    }
    

    4.2 调用

    TimeUnit.SECONDS.sleep(1);
    System.out.println("\n----------【2、继承变量线程】----------");
    for (int i = 0; i < 3; i++) {
        new Thread(new InheritableThread(), "Thread" + i).start();
    }
    

    4.3 输出

    ----------【2、继承变量线程】----------
    [当前线程-子线程]:【Thread2-child】,[线程变量]:【Thread2内容】,匹配结果:true
    [当前线程-子线程]:【Thread0-child】,[线程变量]:【Thread0内容】,匹配结果:true
    [当前线程-子线程]:【Thread1-child】,[线程变量]:【Thread1内容】,匹配结果:true
    [当前线程-父线程]:【Thread2】,[线程变量]:【Thread2内容】,匹配结果:true
    [当前线程-父线程]:【Thread0】,[线程变量]:【Thread0内容】,匹配结果:true
    [当前线程-父线程]:【Thread1】,[线程变量]:【Thread1内容】,匹配结果:true
    

    5 线程池的线程继承

    5.1 依赖

    使用阿里巴巴的transmittable-thread-local,简称TTL。使用了代理模式来实现此功能。

    官网地址:https://github.com/alibaba/transmittable-thread-local

    maven依赖如下:

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

    5.2 定义

    /**
     * 传输变量线程
     */
    public class TransmittableThread implements Runnable {
        
        /**
         * 被TTL修饰的线程池
         */
        private static ExecutorService EXECUTOR_SERVICE = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(2));
        
        /**
         * 继承线程传递-线程变量
         */
        private static final ThreadLocal<String> TRANSMITTABLE_THREAD_LOCAL = new TransmittableThreadLocal<>();
        
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            
            // (1)设置线程变量内容
            String value = threadName + "内容";
            TRANSMITTABLE_THREAD_LOCAL.set(value);
            
            // (2)执行子线程
            EXECUTOR_SERVICE.execute(() -> {
                String childThreadName = Thread.currentThread().getName();
                System.out.println("[当前线程-子线程]:【" + childThreadName + "】,[线程变量]:【" + TRANSMITTABLE_THREAD_LOCAL.get() + "】,匹配结果:" + (value.equals(TRANSMITTABLE_THREAD_LOCAL.get())));
                TRANSMITTABLE_THREAD_LOCAL.remove();
            });
            
            // 让子线程先执行(测试使用)
            try {
                TimeUnit.MICROSECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            // (3)输出线程变量内容
            System.out.println("[当前线程-父线程]:【" + threadName + "】,[线程变量]:【" + TRANSMITTABLE_THREAD_LOCAL.get() + "】,匹配结果:" + (value.equals(TRANSMITTABLE_THREAD_LOCAL.get())));
            
            // (4)清空线程变量内容
            TRANSMITTABLE_THREAD_LOCAL.remove();
            
            // 关闭线程池
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            EXECUTOR_SERVICE.shutdown();
        }
    }
    

    5.3 调用

    TimeUnit.SECONDS.sleep(1);
    System.out.println("\n----------【3、传输变量线程】----------");
    for (int i = 0; i < 3; i++) {
        new Thread(new TransmittableThread(), "Thread" + i).start();
    }
    

    5.4 输出

    ----------【3、传输变量线程】----------
    [当前线程-子线程]:【pool-1-thread-1】,[线程变量]:【Thread2内容】,匹配结果:true
    [当前线程-子线程]:【pool-1-thread-2】,[线程变量]:【Thread0内容】,匹配结果:true
    [当前线程-子线程]:【pool-1-thread-1】,[线程变量]:【Thread1内容】,匹配结果:true
    [当前线程-父线程]:【Thread1】,[线程变量]:【Thread1内容】,匹配结果:true
    [当前线程-父线程]:【Thread2】,[线程变量]:【Thread2内容】,匹配结果:true
    [当前线程-父线程]:【Thread0】,[线程变量]:【Thread0内容】,匹配结果:true
    

    6 校验子线程变量更改(补充)

    校验2.2

    因此ThreadLocal中定义的泛型如果不是String基本类型包装类型等特殊类型,子线程中更改了ThreadLocal内的值,父线程中一起变更,因为两个ThreadLocal中的存储的引用地址一样,对应的是同一个对象,同理,父线程内的ThreadLocal中的值变更后,子线程内的值也会变更。。

    6.1 定义

    /**
     * 继承变量线程(校验传递的是值引用)
     */
    public class InheritableThreadExt implements Runnable {
        
        /**
         * 继承线程传递-线程变量
         */
        private static final ThreadLocal<StringBuilder> INHERIABLE_THREAD_LOCAL = new InheritableThreadLocal<>();
        
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            
            // (1)设置线程变量内容
            StringBuilder value = new StringBuilder(threadName + "内容");
            INHERIABLE_THREAD_LOCAL.set(value);
            
            // (2)执行子线程
            new Thread(() -> {
                // 子线程内容更改线程变量
                INHERIABLE_THREAD_LOCAL.set(INHERIABLE_THREAD_LOCAL.get().append("-child append"));
                
                String childThreadName = Thread.currentThread().getName();
                System.out.println("[当前线程-子线程]:【" + childThreadName + "】,[线程变量]:【" + INHERIABLE_THREAD_LOCAL.get().toString() + "】,匹配结果:" + (value.toString().equals(INHERIABLE_THREAD_LOCAL.get().toString())) + ",父线程变量:" + value.toString());
                INHERIABLE_THREAD_LOCAL.remove();
            }, threadName + "-child").start();
            
            // 让出当前执行线程
            Thread.yield();
            
            // (3)输出线程变量内容
            System.out.println("[当前线程-父线程]:【" + threadName + "】,[线程变量]:【" + INHERIABLE_THREAD_LOCAL.get() + "】,匹配结果:" + (value.equals(INHERIABLE_THREAD_LOCAL.get())));
            
            // (4)清空线程变量内容
            INHERIABLE_THREAD_LOCAL.remove();
        }
    }
    

    6.2 调用

    TimeUnit.SECONDS.sleep(1);
    System.out.println("\n----------【4-1、继承变量线程(校验引用的值传递-对象类型)】----------");
    for (int i = 0; i < 3; i++) {
        new Thread(new InheritableThreadExt(), "Thread" + i).start();
    }
    

    6.3 输出

    ----------【4-1、继承变量线程(校验引用的值传递-对象类型)】----------
    [当前线程-父线程]:【Thread0】,[线程变量]:【Thread0内容】,匹配结果:true
    [当前线程-父线程]:【Thread2】,[线程变量]:【Thread2内容】,匹配结果:true
    [当前线程-父线程]:【Thread1】,[线程变量]:【Thread1内容】,匹配结果:true
    [当前线程-子线程]:【Thread0-child】,[线程变量]:【Thread0内容-child append】,匹配结果:true,父线程变量:Thread0内容-child append
    [当前线程-子线程]:【Thread1-child】,[线程变量]:【Thread1内容-child append】,匹配结果:true,父线程变量:Thread1内容-child append
    [当前线程-子线程]:【Thread2-child】,[线程变量]:【Thread2内容-child append】,匹配结果:true,父线程变量:Thread2内容-child append
    

    相关文章

      网友评论

        本文标题:java线程间ThreadLocal的传递

        本文链接:https://www.haomeiwen.com/subject/oigdfltx.html