1、思路
创建线程池时,我们需要一个创建线程的工厂类,一般都是重写这个工厂类来实现的,这里我们用一个更简单的方法。
线程在执行前,可以先通过MDC.getCopyOfContextMap()获取父线程的MDC的拷贝,执行时,判断这个Map是否为空,不为空设置到自己的线程的MDC即可。重写工厂类就是把这段关键代码“植入”到线程类里,其实还有一个时机做这样的事情,也就是在Runnable在提交到线程池的时候。
2. 创建MDC装饰器
实现Runnable和Callable的装饰器类:
import org.slf4j.MDC;import java.util.Map;
import java.util.concurrent.Callable;public class MDCRunnable implements Runnable {private final Runnable runnable;private final Map<String, String> contextMap;public MDCRunnable(Runnable runnable) {this.runnable = runnable;this.contextMap = MDC.getCopyOfContextMap();}@Overridepublic void run() {if (contextMap != null) {MDC.setContextMap(contextMap);}try {runnable.run();} finally {MDC.clear();}}
}public class MDCCallable<V> implements Callable<V> {private final Callable<V> callable;private final Map<String, String> contextMap;public MDCCallable(Callable<V> callable) {this.callable = callable;this.contextMap = MDC.getCopyOfContextMap();}@Overridepublic V call() throws Exception {if (contextMap != null) {MDC.setContextMap(contextMap);}try {return callable.call();} finally {MDC.clear();}}
}
3. 配置线程池
创建自定义的线程池配置类:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;@Configuration
public class ThreadPoolConfig {@Bean(name = "customThreadPool")public Executor threadPoolTaskExecutor() {// 这里按照常规方法正常创建你的线程池ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);// 这里返回一个匿名类,只重写了execute方法 return new Executor() {@Overridepublic void execute(Runnable command) {// 这里把线程包装一下executor.execute(new MDCRunnable(command));}};}
}
总结
以上两个wapper,包括这个匿名线程池也可以显示定义出来,不难看出他们都是代理类,应用了代码模式进行增强。 这个匿名线程池类在execute前,把线程用MDCRunnable包装了一个,于是线程就有了自动拷贝MDC的能力,目前这是最简单的一种方式。