美文网首页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