因为MDC底层是用ThreadLocal实现的,所以这里补充一些和ThreadLocal相关的知识点。

1.ThreadLocal的三个层次

关于ThreadLocal有三个层次,可以按照这三个层次去理解就不会乱。

三个层次
 * 第一层是Thread空间,通过Thread.currentThread()获得。
 * 第二层是Thread中的两个ThreadLocalMap,threadLocals和inheritableThreadLocals,访问thread对应的两个ThreadLocalMap成员变量获得。
 * 第三层是每个ThreadLocalMap中key——ThreadLocal和value——ThreadLocal的set方法set的值,在get方法中用ThreadLocal的this作为ThreadLocalMap的key获取value。
 * 无论什么操作都要按照这三个层次依次进行才不会乱

2.ThreadLocalMap

保存了当前Thread中存放的ThreadLocal和ThreadLocal对应的值的键值对。
当前线程的所有ThreadLocal变量组成了这个map的keyset,对应的值组成了这个map的valueset。

3.ThreadLocal的操作

第一步都是先用Thread.currentThread()获得当前线程,然后getMap获取线程中的ThreadLocalMap。
像getMap这些操作方法都是包可见性的,包外部无法操作。以ThreadLocal的get方法举例,
  1. /**
  2. * Returns the value in the current thread's copy of this
  3. * thread-local variable.  If the variable has no value for the
  4. * current thread, it is first initialized to the value returned
  5. * by an invocation of the {@link #initialValue} method.
  6. *
  7. * @return the current thread's value of this thread-local
  8. */
  9. public T get() {
  10. Thread t = Thread.currentThread();
  11. ThreadLocalMap map = getMap(t);
  12. if (map != null) {
  13. ThreadLocalMap.Entry e = map.getEntry(this);
  14. if (e != null)
  15. return (T)e.value;
  16. }
  17. return setInitialValue();
  18. }
  1. /**
  2. * Returns the value in the current thread's copy of this
  3. * thread-local variable.  If the variable has no value for the
  4. * current thread, it is first initialized to the value returned
  5. * by an invocation of the {@link #initialValue} method.
  6. *
  7. * @return the current thread's value of this thread-local
  8. */
  9. public T get() {
  10. Thread t = Thread.currentThread();
  11. ThreadLocalMap map = getMap(t);
  12. if (map != null) {
  13. ThreadLocalMap.Entry e = map.getEntry(this);
  14. if (e != null)
  15. return (T)e.value;
  16. }
  17. return setInitialValue();
  18. }

4.InheritableThreadLocal

Thread类中有两个ThreadLocalMap,一个是threadLocals,一个是inheritableThreadLocals。
threadLocals保存的是当前线程中的ThreadLocal变量们,inheritableThreadLocals保存的是当前线程父线程中的变量们。
InheritableThreadLocal类覆写了getMap和createMap这两个方法,
  1. /**
  2. * Get the map associated with a ThreadLocal.
  3. *
  4. * @param t the current thread
  5. */
  6. ThreadLocalMap getMap(Thread t) {
  7. return t.inheritableThreadLocals;
  8. }
  1. /**
  2. * Get the map associated with a ThreadLocal.
  3. *
  4. * @param t the current thread
  5. */
  6. ThreadLocalMap getMap(Thread t) {
  7. return t.inheritableThreadLocals;
  8. }
  1. /**
  2. * Create the map associated with a ThreadLocal.
  3. *
  4. * @param t the current thread
  5. * @param firstValue value for the initial entry of the table.
  6. * @param map the map to store.
  7. */
  8. void createMap(Thread t, T firstValue) {
  9. t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
  10. }
  1. /**
  2. * Create the map associated with a ThreadLocal.
  3. *
  4. * @param t the current thread
  5. * @param firstValue value for the initial entry of the table.
  6. * @param map the map to store.
  7. */
  8. void createMap(Thread t, T firstValue) {
  9. t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
  10. }

可以看出在初始化或者getMap的时候,获取到的都是inheritableThreadLocals引用,操作的也是inheritableThreadLocals这个ThreadLocalMap。

5.threadLocals和inheritableThreadLocals的初始化

  1. /**
  2. * Initializes a Thread.
  3. *
  4. * @param g the Thread group
  5. * @param target the object whose run() method gets called
  6. * @param name the name of the new Thread
  7. * @param stackSize the desired stack size for the new thread, or
  8. *        zero to indicate that this parameter is to be ignored.
  9. */
  10. private void init(ThreadGroup g, Runnable target, String name,
  11. long stackSize) {
  12. ...
  13. Thread parent = currentThread();
  14. ...
  15. if (parent.inheritableThreadLocals != null)
  16. this.inheritableThreadLocals =
  17. ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
  18. ...
  19. }
  1. /**
  2. * Initializes a Thread.
  3. *
  4. * @param g the Thread group
  5. * @param target the object whose run() method gets called
  6. * @param name the name of the new Thread
  7. * @param stackSize the desired stack size for the new thread, or
  8. *        zero to indicate that this parameter is to be ignored.
  9. */
  10. private void init(ThreadGroup g, Runnable target, String name,
  11. long stackSize) {
  12. ...
  13. Thread parent = currentThread();
  14. ...
  15. if (parent.inheritableThreadLocals != null)
  16. this.inheritableThreadLocals =
  17. ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
  18. ...
  19. }

在new一个Thread的时候会调用Thread的init方法,该方法中如果parent线程的inheritableThreadLocals不是null的话,就会用createInheritedMap方法,用parent的inheritableThreadLocals中的元素构造一个新的ThreadLocalMap。

注意:该操作只在线程初始化的时候进行,所以在该线程初始化之后,parent线程对parent线程自己的inheritableThreadLocals变量的操作不会影响到当前线程的inheritableThreadLocals了,因为已经不是同一个map了。
MDC就是利用这个InheritableThreadLocal把父线程的context带到子线程中,把上下文传递到子线程中通过日志输出,把一次完整的请求串联起来。

6.parent线程

  1. Thread parent = currentThread();
  1. Thread parent = currentThread();

在Thread的init方法中,是通过获得当前线程作为parent线程,也就是说,在哪个线程中new的这个Thread并start的,执行该操作的线程就是new的新Thread的parent线程。

但是init方法只在线程初始化的时候执行一次,所以如果用的线程池来使线程重用的话,就不会再调用这个init方法了,这会带来一些问题,后面会具体说。

ThreadLocal MDC的更多相关文章

  1. 白话TCP/IP原理

    TCP/IP(Transmission-Control-Protocol/Internet-Protocol),中文译名为传输控制协议/因特网互联协议,又名网络通讯协议,是Internet最基本的协议 ...

  2. slf4j MDC使用

    slf4j MDC使用 最近也是在项目代码里发现一个地方有个MDC.put(),忍不住好奇点了进去,于是知道了MDC这个东西,细研究一下,发现还真是个好东西. MDC解决了什么问题 MDC全名Mapp ...

  3. Slf4j MDC 使用和 基于 Logback 的实现分析

    前言 如今,在 Java 开发中,日志的打印输出是必不可少的, 关于  有了日志之后,我们就可以追踪各种线上问题.但是,在分布式系统中,各种无关日志穿行其中,导致我们可能无法直接定位整个操作流程.因此 ...

  4. logback MDC(Mapped Diagnostic Context)与分布式系统的跟踪系统

    logback MDC(Mapped Diagnostic Context)与分布式系统的跟踪系统 logback官方文档中第8章Mapped Diagnostic Context给我们提供了一些分布 ...

  5. 多线程之美2一ThreadLocal源代码分析

    目录结构 1.应用场景及作用 2.结构关系 2.1.三者关系类图 2.2.ThreadLocalMap结构图 2.3. 内存引用关系 2.4.存在内存泄漏原因 3.源码分析 3.1.重要代码片段 3. ...

  6. Hystrix实现ThreadLocal上下文的传递 转

    springcloud微服务中, 服务间传输全局类参数,如session信息等. 一.问题背景 Hystrix有2个隔离策略:THREAD以及SEMAPHORE,当隔离策略为 THREAD 时,是没办 ...

  7. MDC 输出线程信息帮助定位问题

    log4j中的%x ---NDC,%X---MDC 即%x NDC.clear();NDC.push(this.toString());%X{first} %X{last}MDC.put(" ...

  8. MDC是什么鬼?用法、源码一锅端

    近期用到阿里的一款开源的数据同步工具 Canal,不经意之中看到了 MDC 的用法,而且平时项目中也多次用到 MDC,趁机科普一把. 通过今天的分享,能让你轻松 get 如下几点,绝对收获满满. a) ...

  9. ThreadLocal 是什么鬼?用法、源码一锅端

    ThreadLocal 是一个老生常谈的问题,在源码学习以及实际项目研发中,往往都能见到它的踪影,用途比较广泛,所以有必要深入一番. 敢问,ThreadLocal 都用到了哪里?有没有运用它去解决过业 ...

随机推荐

  1. 自定义View,随着手指运动的小球

    这个实例是自定的view的初步介绍,要设计的是一个随着手指运动的小球.原理是随时获取手指的坐标,然后在这个坐标上面实时改变自定义view的坐标.这个view仅仅是画了一个圆形而已. 自定义的view ...

  2. [Web 前端] 前端频道之团队维护、聚合、订阅

    cp from :https://blog.csdn.net/ivan820819/article/details/78885404 国内 腾讯 ISUX 腾讯全端 AlloyTeam 奇舞周刊 阿里 ...

  3. [Android Security] APK自我保护 - DEX/APK校验

    cp : https://segmentfault.com/a/1190000005105973 DEX校验 classes.dex 是 Android 虚拟机的可执行文件,我们所写的 java 代码 ...

  4. [Hook] 免root,自己进程内,binder hook (ClipboardManager)

    cp from : http://weishu.me/2016/02/16/understand-plugin-framework-binder-hook/ Android系统通过Binder机制给应 ...

  5. ThreadLocal 与 Request 和 Session 之关联

    ThreadLocal<T> 类行是 .NET Framework 4.0 才开始支持的类型,MSDN 的解释只有简单的一句话:提供数据的线程本地存储,其提供的实例代码也不太好,有点为了演 ...

  6. [转]PHP SOCKET编程

    FROM : http://blog.csdn.net/hguisu/article/details/7448528 1. 预备知识 一直以来很少看到有多少人使用php的socket模块来做一些事情, ...

  7. PHP 7中利用OpenSSL代替Mcrypt加解密的方法详解

    php7.1发布后新特性吸引了不少PHPer,大家都在讨论新特性带来的好处与便利.但是从php7.0 升级到 php7.1 废弃(过时)了一个在过去普遍应用的扩展(mcrypt扩展).官方提供了相应的 ...

  8. Netflix推荐系统:从评分预测到消费者法则

    http://in.sdo.com/?p=11 原文链接:Netflix recommendations: beyond the 5 stars (Part 1), (Part 2) 原文作者:Xav ...

  9. svn commit --cl app 时手动输入提交的注释,而不是在 -m 'comments here'这里输入

    原来只需要,提交的时候不指定 -m ,也不指定 -F就可以了,提交之前,svn会自动弹出编辑框来,可以修改信息. https://stackoverflow.com/questions/1746891 ...

  10. Eclipse QuickSear的插件的说明

    https://spring.io/blog/2013/07/11/eclipse-quick-search Eclipse QuickSear的插件的说明