天天看点

log(四)——MDC使用

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方法。

log