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
网友评论