因为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. vue quill editor输入文字出现首字母的问题

    当使用vue quill editor输入中文时,第一个中文的汉语拼音第一个字母会显示如图. 解决的办法就是升级vue quill editor js文件的版本,目前的我升级之后ok的版本是 < ...

  2. 关于GreenPlum的一些整理

    Greenplum数据库架构 Greenplum数据库基本由PostgreSQL核心增强数据库实例组合并衔接成的数据库管理系统,即Greenplum数据在PostgreSQL基础上扩展开发,每个Gre ...

  3. 局部响应归一化(Local Response Normalization,LRN)

     版权声明:本文为博主原创文章,欢迎转载,注明地址. https://blog.csdn.net/program_developer/article/details/79430119 一.LRN技术介 ...

  4. Server 2008 R2大改造变成梦幻Win7系统

    在此之前先补充一下知识Windows Server 2008和Windows Server 2008 R2的不同之处Windows Server 2008是基准与Vista的内核构建的,支持X86框架 ...

  5. 文件 File 常见操作 工具 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  6. 大数据开发实战:Stream SQL实时开发三

    4.聚合操作 4.1.group by 操作 group by操作是实际业务场景(如实时报表.实时大屏等)中使用最为频繁的操作.通常实时聚合的主要源头数据流不会包含丰富的上下文信息,而是经常需要实时关 ...

  7. Jsonp 关键字详解及json和jsonp的区别,ajax和jsonp的区别

    为什么要用jsonp? 相信大家对跨域一定不陌生,对同源策略也同样熟悉.什么,你没听过?没关系,既然是深入浅出,那就从头说起. 假如我写了个index页面,页面里有个请求,请求的是一个json数据(不 ...

  8. AI 也开源:50 大开源 AI 项目 (转)

    这些开源AI项目专注于机器学习.深度学习.神经网络及其他应用场合. 自IT界早期以来,研制出能像人类那样“思考”的机器一直是研究人员的一大目标.在过去几年,计算机科学家们在人工智能(AI)领域已取得了 ...

  9. PowerShell获取当前用户的权限

      function Get-CurrentUserRoles {   $SecurityPrinciple = New-Object -TypeName System.Security.Princi ...

  10. Git直接拉取远程分支

    用Git,一直有个疑惑,可不可以不拉取远程Origin主干,我直接pull一个分支下来 今天想了一下,找到了一个办法 本地分支关联 // 0.新建一个文件夹,然后初始化git git init // ...