因为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. SQL:Oracle 目录

    ylbtech-Miscellaneos-SQL:Oracle 目录 Oracle Database,又名Oracle RDBMS,或简称Oracle.是甲骨文公司的一款关系数据库管理系统.它是在数据 ...

  2. C#如何通过反射调用带有ref或者out的参数的方法

    写这篇博客,原起今天CyljXu问我一个问题:如何通过反射调用带有ref或者out的参数的方法?想着可能其他人也会遇到这个问题,权且记录下来,以备后行者搜索到. 这个在MSDN上有解释,参考 Meth ...

  3. webpack打包器简单入门

    概念 webpack是一个现代javascript应用程序的模块打包器. 当webpack处理你的应用程序时,它会递归构建一个依赖图(包含了你的应用程序所需要每个模块),然后把这些模块打包到少数几个b ...

  4. IOS中的网络编程详解

    在移动互联网时代,几乎所有应用都需要用到网络,比如QQ.微博.网易新闻.优酷.百度地图,只有通过网络跟外界进行数据交互.数据更新,应用才能保持新鲜.活力,如果没有了网络,也就缺少了数据变化,无论外观多 ...

  5. Java 读取某个目录下所有文件、文件夹

    /** * @Author: * @Description:获取某个目录下所有直接下级文件,不包括目录下的子目录的下的文件,所以不用递归获取 * @Date: */ public static Lis ...

  6. Java面试问题总结

    前几天Java面试中遇到的问题,这仅仅是当中的一部分问题.面试中有非常多问题是关于数据结构和算法的.在这里做下总结,希望有能力的人能够试着做一下,并在评论区留下您的答案.让大家相互学习.谢谢 程序设计 ...

  7. Python机器学习——线性模型

    http://www.dataguru.cn/portal.php?mod=view&aid=3514 摘要 : 最近断断续续地在接触一些python的东西.按照我的习惯,首先从应用层面搞起, ...

  8. Linq-单条数据删除

    单条数据删除,用DeleteOnSubmit NorthwindDataContext db = new NorthwindDataContext(); Customer test1 = ctx.Cu ...

  9. linux下判断文件和目录是否存在[总结]

    1.前言 工作中涉及到文件系统,有时候需要判断文件和目录是否存在.我结合APUE第四章文件和目录,总结一下如何正确判断文件和目录是否存在,方便以后查询. 2.stat系列函数 stat函数用来返回与文 ...

  10. 跨域资源共享(CORS)--跨域ajax

    几年前,网站开发者都因为ajax的同源策略而撞了南墙.当我们惊叹于XMLHttpRequest对象跨浏览器支持所带来的巨大进步时,我们很快发现没有一个方法可以使我们用JavaScript实现请求跨域访 ...