因为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. COUNT DISTINCT ROW_NUMBER DENSE_RANK 以及对COUNT去重(非PARTITION)

    1:COUNT DISTINCT         SELECT          COUNT(DISTINCT [QS_QuestionStem].Id)  AS ReqCount1,         ...

  2. Java反编译工具CFR,Procyon简介

    Java反编译工具有很多,个人觉得使用最方便的是jd-gui,当然jad也不错,jd-gui主要提供了图形界面,操作起来很方便,但是jd-gui很久没有更新了,java 7出来很久了,jd-gui在反 ...

  3. Petri网

    Petri网是一种适合于系统描述和分析的数学模型,主要描述异步和并发关系.(或者Petri网是对离散并行系统的数学表示,适用于描述异步的,并发的计算机系统模型.) Petri网模型自然,直观,简单易懂 ...

  4. jQuery中attr和prop方法的区别说明

    jquery中attr和prop的基本区别可以理解为:如果是内置属性,建议用prop,如果是自定义的建议用attr. 例如 <input type=check  node=123 id=ck & ...

  5. android中解决“Dex Loader] Unable to execute dex: Multiple dex files define LXXX”错误

    原因 1. 出现这种问题的主要原因:那就是你的libs下面引用了两个相同功能的包,可能这两个包的版本不一样而已,去掉一个吧,选择一个合适的版本. 2.build path里面包重复引用.

  6. 小游戏:HelloColor

    这是我写的第一个游戏.模仿一款手机游戏"颜色运行"写的.大概花了一天的时间完成,挺简单的. 游戏名:HelloColor,翻译成中文是:你好色 按空格键开始和暂停开始游戏后,界面右 ...

  7. 第一章 Java加解密简介

    1.加密算法: 移位.替代(古典加密) 对称加密:DES.AES 非对称加密:RSA 散列函数算法(单向加密):MD5.SHA.Mac 数字签名算法:RSA.DSA 其中,前三种主要完成数据的加解密: ...

  8. cesium and three.js【转】

    https://blog.csdn.net/zhishiqu/article/details/79077883 这是威尔逊Muktar关于整合Three.js与铯的客人帖子.Three.js是一个轻量 ...

  9. [leetcode]Gray Code @ Python

    原题地址:https://oj.leetcode.com/problems/gray-code/ 题意: The gray code is a binary numeral system where ...

  10. extern外部方法使用C#简单例子

    外部方法使用C#简单例子 1.增加引用using System.Runtime.InteropServices; 2.声明和实现的连接[DllImport("kernel32", ...