1.MDC put
用MDC的put方法,把需要的context添加到当前线程的context map中。
之前说过,子线程在创建的时候会把父线程中的inheritableThreadLocals变量设置到子线程的inheritableThreadLocals中,而MDC内部是用InheritableThreadLocal实现的,所以自然会把父线程中的上下文带到子线程中。
2.getCopyOfContextMap
对于线程池中的线程来说,这部分线程是可以重用的,但是线程本身只会初始化一次,所以之后重用线程的时候,就不会进行初始化操作了,也就不会有上一段中提到的父线程inheritableThreadLocals拷贝到子线程中的过程了。
这个时候如果还想传递父线程的上下文的话,就要使用getCopyOfContextMap方法。
/**
* Return a copy of the current thread's context map. Returned value may be
* null.
*/
public Map getCopyOfContextMap() {
lastOperation.set(READ_OPERATION);
Map<String, String> hashMap = copyOnInheritThreadLocal.get();
if (hashMap == null) {
return null;
} else {
return new HashMap<String, String>(hashMap);
}
}
该方法会把当前线程的context制作一份副本返回。
ExecutorService.execute(new Runnable())的时候,在Runnable构造的时候,用这个方法得到一个Map,保存起来,这时的context是父线程的。
然后在执行run方法的时候,放到MDC中去——子线程的context map中去。
可以自己封装一个AbstractRunnable类,对Runnable的run方法进行一个包装,
public abstract class MdcRunnable implements Runnable {
/**
* 为了线程池中的线程在复用的时候也能获得父线程的MDC中的信息,
* 子线程第一次初始化的时候没事,因为通过InheritableThreadLocal
* 已经可以获得MDC中的内容了
*/
private final Map mdcContext = MDC.getCopyOfContextMap();
@Override
public final void run() {
// 线程重用的时候,把父线程中的context map内容带入当前线程的context map中,
// 因为线程已经初始化过了,不会像初始化时那样通过拷贝父线程inheritableThreadLocals到子线程
// 的inheritableThreadLocals操作来完成线程间context map的传递。
// 真正执行到这个run方法的时候,已经到了子线程中了,所以要在初始化的时候用
// MDC.getCopyOfContextMap()来获得父线程contest map,那时候还在父线程域中
if (mdcContext != null) {
MDC.setContextMap(mdcContext);
}
try {
runWithMdc();
} finally {
MDC.clear();
}
}
protected abstract void runWithMdc();
}
用户真正需要实现的执行任务的方法是runWithMdc方法。