一、ThreadLocal
1、说明
ThreadLocal 是一个线程局部变量。其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。
2、使用方法
ThreadLocal<String> nameThreadLocal = new ThreadLocal<>();
nameThreadLocal.set(name);
nameThreadLocal.get();
3、Request 使用案例
在springMvc
中单例controller
中定义成员变量 request
,使用@autowired
注入时,request
就是使用ThreadLocal
实现线程安全的。
- 在controller中注入的request是jdk动态代理对象,ObjectFactoryDelegatingInvocationHandler的实例.当我们调用成员域request的方法的时候其实是调用了objectFactory的getObject()对象的相关方法.这里的objectFactory是RequestObjectFactory.
- RequestObjectFactory的getObject其实是从RequestContextHolder的threadlocal中去取值的.
- 请求刚进入springmvc的dispatcherServlet的时候会把request相关对象设置到RequestContextHolder的threadlocal中去.
参考:在SpringMVC Controller中注入Request成员域 - abcwt112 - 博客园
4、参考
ThreadLocal详解
https://www.jianshu.com/p/3bb70ae81828
二、InheritableThreadLocal
1、说明
ThreadLocal设计之初就是为了绑定当前线程,如果希望当前线程的ThreadLocal能够被子线程使用,实现方式就会相当困难(需要用户自己在代码中传递)。在此背景下,InheritableThreadLocal应运而生。
2、原理
创建新的线程时,会调用以下的构造方法。
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
我们沿着构造方法中的init方法,可以找到这样一段代码。就是在这里创建的 inheritableThreadLocals 。
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
继续查看 createInheritedMap 方法,里面新建了一个 ThreadLocalMap 对象,其构造方法如下。
分析代码我们可知,在创建子线程时,会将父线程的 inheritableThreadLocals 复制到子线程的 inheritableThreadLocals 对象。
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) {
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++;
}
}
}
}
3、参考
InheritableThreadLocal详解
https://www.jianshu.com/p/94ba4a918ff5
三、TransmittableThreadLocal
1、概述
使用线程池时,线程会被复用,因此线程池中的线程,执行任务时如果要获取 提交线程(即提交任务到线程池
的线程) 保存的对象,则可以使用 TransmittableThreadLocal
。
2、使用方法
TransmittableThreadLocal<String> ttl = new TransmittableThreadLocal<>();
ThreadPoolExecutor executor = new ThreadPoolExecutor(ThreadSize, ThreadSize, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(10));
// 设置变量,并将任务提交到线程池
ttl.set("ThreadA-TTL");
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println(ttl.get());
}
};
// 生成修饰后的对象ttlRunnable。
task = TtlRunnable.get(task);
executor.submit(task);
3、案例(业务侵入式)
(1)代码
package com.scy.example.controller;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.TtlRunnable;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Test02 {
static ThreadPoolExecutor executor = null;
static TransmittableThreadLocal<String> ttl = new TransmittableThreadLocal<>();
public static void main(String[] args) {
try {
// 在A线程中创建线程池,并且启动线程池中的线程
Thread threadA = new Thread(() -> {
int ThreadSize = 1;
executor = new ThreadPoolExecutor(ThreadSize, ThreadSize, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(10));
ttl.set("ThreadA-TTL");
// 启动线程池中的线程
for (int i = 0; i < ThreadSize; i++) {
executor.submit(() -> {
System.out.println("初始化线程完毕!");
});
}
});
threadA.start();
// 等待线程池中的线程启动完毕
Thread.sleep(100);
// 在B线程中,向线程池提交任务
Thread threadB = new Thread(() -> {
ttl.set("ThreadB-TTL");
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println(ttl.get());
}
};
// 额外的处理,生成修饰了的对象ttlRunnable
task = TtlRunnable.get(task);
executor.submit(task);
});
threadB.start();
Thread.sleep(100);
executor.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
(2)结果
初始化线程完毕!
ThreadB-TTL
(3)说明
使用 TransmittableThreadLocal
存储对象时;
如果任务没有使用 TtlRunnable
修饰,则 TransmittableThreadLocal
相当于 InheritableThreadLocal
;此时在线程池中执行任务时获取对象,获取的是 创建线程(即创建线程池中线程
的线程) 保存的对象。
如果任务使用了 ttlRunnable
修饰,此时在线程池中执行任务时获取对象,获取的是 提交线程(即提交任务到线程池
的线程) 保存的对象。而且无法获取创建线程保存的对象。
4、案例(agent实现)
需配置启动参数-javaagent:path/to/transmittable-thread-local-2.x.x.jar
-javaagent:D:\maven\mvnRespo\com\alibaba\transmittable-thread-local\2.13.2\transmittable-thread-local-2.13.2.jar
4、参考
TransmittableThreadLocal(TTL)实现线程变量传递的原理分析_未完成交响曲-KyleWang的博客-CSDN博客_transmittablethreadlocal原理
TransmittableThreadLocal使用JavaAgent动态代理机制分析_Lidisam的博客-CSDN博客
分布式链路跟踪技术(五):跨线程传输和上下文传播 - 百度文库 (baidu.com)
四、TaskDecorator
1、概述
使用线程池时,线程会被复用,因此线程池中的线程,执行任务时如果要获取 提交线程(即提交任务到线程池
的线程) 保存的对象,还可以使用 TaskDecorator
。看这个名称大概就能猜出是一个装饰器设计原理。
spring 4.3 提供 TaskDecorator
2、使用方法
主线程16个,子线程2个,执行10次,目的是尽可能让子线程复用。
public class TaskDecoratorTest {
public static void main(String[] args) {
new TaskDecoratorTest().testThreadLocal();
}
public void testThreadLocal() {
ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
ExecutorService mainThreadPool = Executors.newFixedThreadPool(16);
ThreadPoolTaskExecutor childThreadPool = new ThreadPoolTaskExecutor();
childThreadPool.setCorePoolSize(2);
childThreadPool.setMaxPoolSize(2);
childThreadPool.setTaskDecorator(runnable -> {
int v = threadLocal.get();
System.out.println("装饰器中获取到主线程=" + Thread.currentThread().getName() + " 获取上下文=" + v);
return () -> {
try {
//重新copy传递给子线程
threadLocal.set(v);
runnable.run();
} finally {
threadLocal.remove();
}
};
});
childThreadPool.initialize();
for (int i = 0; i < 10; i++) {
int finalI = i;
mainThreadPool.execute(() -> {
//模拟在主线程设置上下文变量
threadLocal.set(finalI);
childThreadPool.execute(() -> System.out.println("子线程" + Thread.currentThread().getName() + " 获取上下文变量=" + threadLocal.get()));
threadLocal.remove();
});
}
try {
childThreadPool.getThreadPoolExecutor().awaitTermination(3, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3、原理
@Override
protected ExecutorService initializeExecutor(
ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
BlockingQueue<Runnable> queue = createQueue(this.queueCapacity);
ThreadPoolExecutor executor;
//当线程池的装饰器不为空时,执行execute方法会进入这里,因为它重写了execute方法
if (this.taskDecorator != null) {
executor = new ThreadPoolExecutor(
this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
queue, threadFactory, rejectedExecutionHandler) {
//这里是一个代理设计模式的实现,对execute做了一层代理
@Override
public void execute(Runnable command) {
//执行装饰器的逻辑,注意这段代码是在主线程中运行
Runnable decorated = taskDecorator.decorate(command);
if (decorated != command) {
decoratedTaskMap.put(decorated, command);
}
//子线程真正执行的方法(异步模块)...初始化核心线程数,核心线程满了入队列,队列满开启至最大线程数
super.execute(decorated);
}
};
}
else {
executor = new ThreadPoolExecutor(
this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
queue, threadFactory, rejectedExecutionHandler);
}
if (this.allowCoreThreadTimeOut) {
executor.allowCoreThreadTimeOut(true);
}
this.threadPoolExecutor = executor;
return executor;
}
4、参考
TaskDecorator解决父子线程间传递上下文数据
https://blog.csdn.net/lmx1989219/article/details/110522077
五、ThreadLocal内存泄漏
https://blog.csdn.net/qq_25775675/article/details/105731434