slf4j的MDC对象和ThreadLocal简单分析

2023-05-16

MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。某些应用程序采用多线程的方式来处理多个用户的请求。在一个用户的使用过程中,可能有多个不同的线程来进行处理。典型的例子是 Web 应用服务器。当用户访问某个页面时,应用服务器可能会创建一个新的线程来处理该请求,也可能从线程池中复用已有的线程。在一个用户的会话存续期间,可能有多个线程处理过该用户的请求。这使得比较难以区分不同用户所对应的日志。当需要追踪某个用户在系统中的相关日志记录时,就会变得很麻烦。

一种解决的办法是采用自定义的日志格式,把用户的信息采用某种方式编码在日志记录中。这种方式的问题在于要求在每个使用日志记录器的类中,都可以访问到用户相关的信息。这样才可能在记录日志时使用。这样的条件通常是比较难以满足的。MDC 的作用是解决这个问题。

MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据。
  MDC的用法时MDC.put(key,value),获取时直接MDC.get(key);

MDC是通过ThreadLocal对象实现的,在此顺便复习下ThreadLocal原理

MDC.put()方法的源码如下:

  public static void put(String key, String val) throws IllegalArgumentException {
        if (key == null) {
            throw new IllegalArgumentException("key parameter cannot be null");
        }
        if (mdcAdapter == null) {
            throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
        }
        mdcAdapter.put(key, val);
    }

可以看到是调用了mdcAdapter.put()方法,mdcAdapter 的声明是static MDCAdapter mdcAdapter,其中MDCAdapter是一个接口类型,有多种实现,我们看一下logback的实现LogbackMDCAdapter,它的put方法源码如下

  public void put(String key, String val) throws IllegalArgumentException {
    if (key == null) {
        throw new IllegalArgumentException("key cannot be null");
    }

    Map<String, String> oldMap = copyOnThreadLocal.get();
    Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);

    if (wasLastOpReadOrNull(lastOp) || oldMap == null) {
        Map<String, String> newMap = duplicateAndInsertNewMap(oldMap);
        newMap.put(key, val);
    } else {
        oldMap.put(key, val);
    }
}

可以看到就是根据规定的操作选择创建新的map对象或者使用旧的map对象,map对象时放入copyOnThreadLocal里,它的声明如下:
final ThreadLocal<Map<String, String>> copyOnThreadLocal = new ThreadLocal<Map<String, String>>();
可以看到MDC实现多线程下区分不同信息就是通过ThreadLocal。

下面顺便来复习下ThreadLocal的实现原理,
threadLocal的使用方式很简单,
ThreadLocal threadLocal = new ThreadLocal();
threadLocal.set(new T());
使用时直接 threadLocal.get()即可;
threadLocal.set方法的源码所示:

 public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

实际上就是把ThreadLocal实例和它要保存的变量放入了ThreadLocalMap 中,ThreadLocalMap是通过getMap()来获得,传入的参数是当前的thread对象,其源码如下:

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

可以看到实际上ThreadLocalMap就是线程对象的一个成员对象,也就是说,ThreadLocal保存线程安全变量的最终实现方式就是把变量存入了Thread对象中。
回到ThreadLocalMap,它是个什么对象呢?现在看看ThreadLocalMap的set方法

private void set(ThreadLocal<?> key, Object value) {
        Entry[] tab = table;
        int len = tab.length;
        int i = key.threadLocalHashCode & (len-1); 
        for (Entry e = tab[i];
             e != null;
             e = tab[i = nextIndex(i, len)]) {
            ThreadLocal<?> k = e.get();
            if (k == key) {
                e.value = value;
                return;
            }
            if (k == null) {
                replaceStaleEntry(key, value, i);
                return;
            }
        }
        tab[i] = new Entry(key, value);
        int sz = ++size;
        if (!cleanSomeSlots(i, sz) && sz >= threshold)
            rehash();
    }

可以看到其实set方法是把数据放入了一个Entry[]数组,而这个Entry[]数组,是ThreadLocalMap的table成员,table 的声明如下:
private Entry[] table;
所以这样看来,ThreadLocalMap维护了一个Entry[]数组来保存ThreadLocal到ThreadLocal所保存对象的映射。
Entry的定义如下:

static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

可以看到实际上就是一个弱引用对象。

综上看来,ThreadLocal的实现原理就是在Thread里存了其本身的弱引用和需存储的变量。

ThreadLocal有时会造成内存泄漏,详见https://blog.csdn.net/zhailuxu/article/details/79067467

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

slf4j的MDC对象和ThreadLocal简单分析 的相关文章

  • 文档-操作手册与用户手册的区别

    背景 我们都已经了解了操作手册和用户手册都是给用户看的 xff0c 那么两者之间的区别是什么呢 xff1f 为什么要产生这两个文档呢 xff1f 详解 1 操作手册是系统级别的文档 xff0c 而用户手册是需求级别的文档 操作手册讲述的是如
  • msOS使用小结

    在做激关打标机的项目的过程中 xff0c 使用msOS遇到了一些问题 xff0c 和解决方法 xff0c 还有一些小体会 xff0c 现在拿出来分享一下 一 代码移植时遇到的问题 xff1a 激光打标机项目用到一块新的板子 xff0c 所以
  • IntelliJ IDEA添加JavaDOC注释 方法 快捷键

    第一种方法 Settings gt Keymap gt Other gt Fix doc comment gt 右键 gt 选择 Add Keyboard Shortcut xff0c 然后输入自定义的快捷键 默认Fix doc comme
  • 关于ElementUI 项目开发的eslint 报错问题的修复

    介绍 使用操作系统为 Win11Win11 使用教程 1 使用 ElementUI 项目进行开发时因为各自的格式化文档不同难免会遇到各种报错 但是大部分都是因为 单引 分号 逗号 的格式存在报错 2 这时我们在项目的根目录下可以新建一个 p
  • CentOS安装lsusb查看设备串号Serial

    安装lsusb span class hljs built in echo span span class hljs string 34 password 34 span span class hljs built in sudo span
  • VMware虚拟机安装CentOS8连不上网问题

    VMware虚拟机安装CentOS8连不上网 改了半天VMware中CentOS的网卡 xff0c 从 桥接模式 改到 NAT模式 34 都不管用 终极解决方案 1 选中你的虚拟机 xff0c 比如我的CentOS8 2 点击菜单栏中的 编
  • Ubuntu更换软件源

    更换 Ubuntu 的软件源 对于Ubuntu系统 xff0c 不同的版本的源都不一样 xff0c 每一个版本都有自己专属的源 而对于 Ubuntu 的同一个发行版本 xff0c 它的源又分布在全球范围内的服务器上 Ubuntu 默认使用的
  • Eclipse本地运行MapReduce

    环境说明 xff1a Linux环境下已搭建好hadoop集群 xff0c windows本地安装Eclipse 远程启动MapReduce任务 1 下载并安装eclipse插件 xff1a hadoop eclipse plugin XX
  • nvm 管理你的node

    1 安装nvm 2 nvm 管理命令 nvm ls remote 可以会列出所有可用的 Nodejs 版本 nvm install v11 0 0 就可以把这个版本的 Nodejs 安装到我们的机器上了 nvm list 可以查看当前已安装
  • ssh免密登录配置+调试讲解(超详细)+原理解析

    有写的不对的地方 xff0c 欢迎各位同学评论指正 xff0c 博主会进行修改 前 言 集群搭建中 xff0c 常常需要配置ssh免密登录 xff0c 而每台机器情况不一样 xff0c 本来博主认为没多少东西 xff0c 结果 xff0c
  • Windows和Ubuntu双系统双引导教程

    一 参考资料 Windows和Ubuntu双系统安装教程 二 步骤 1 下载EasyBCD xff0c 并安装 2 设置Windows引导 3 设置Ubuntu引导 4 启动系统 遇到这种情况 xff0c 直接Enter回车 选择系统
  • 大数据技术原理与应用(第七章 MapReduce)

    目录 7 1 MapReduce简介 MapReduce与传统并行计算框架对比 MapReduce模型 MapReduce策略 MapReduce理念 计算向数据靠拢 MapReduce架构 Master Slave Map函数和Reduc
  • FreeRTOS多任务调度原理(基于Cortex-M4)

    目录 1 Cortex M4中SysTick的重要性 2 Cortex M4中的中断管理 3 Cortex M4中影子栈指针 4 Cortex M4中SVC和PendSV异常 5 多任务启动 6 PendSV业务流程 7 系统时钟节拍详解
  • Pytorch 线性回归 grad清零报错:w.grad.data.zero_() AttributeError: 'NoneType' object has no attribute 'data'

    学习了https github com L1aoXingyu code of learn deep learning with pytorch blob master chapter3 NN linear regression gradie
  • 查看一台机器的vnc端口及vnc是否开启

    netstat lnpt grep Xvnc 查的到端口说明开的 如果没开用命令开 systemctl start vncserver 64 1 service
  • CMMI等级划分和对照

    CMMI xff08 Capability Maturity Model Integration xff09 即软件成熟度集成模型 是力图通过一套模型改善软件质量 xff0c 规范软件过程管理的模型 由于软件开发的随意和变动性比较大 xff
  • 关于解决校园网Drcom经常掉线的问题

    关于解决一些电脑由于使用WIFI共享而导致校园网Drcom经常掉线 xff1a 第一种方式 xff1a 打开控制面板 gt 网络和共享中心 gt 更改适配器设置 xff0c 再找到无线网络连接如下图所示 xff1a 鼠标右键 点开属性栏 找
  • pytorch学习日记(二)——之cv2,matplotlib,PIL比较及与Tensor的转换

    用python进行图像处理中分别用到过matplotlib pyplot PIL cv2三种库 xff0c 这三种库图像读取和保存方法各异 xff0c 并且图像读取时顺序也有差异 xff0c 如plt imread和PIL Image op
  • Python123第七周编程题

    1 文本的平均列数 span class token keyword with span span class token builtin open span span class token punctuation span span c
  • 如何完全卸载PyCharm

    进入bin文件 xff0c 找到uninstall xff0c 双击即可

随机推荐

  • 递归算法的简单示例

    1 xff0c 递归实现sum 函数 span class token keyword def span span class token function sum span span class token punctuation spa
  • 记事本文件保存为JAVA文件

    如何将记事本文件保存为java文件 xff1f 1 xff0c 在将记事本文件保存后 xff0c 在通过将文件名 xff08 以hello为例 xff09 后缀改为 java后 xff0c 通过查看其属性发现其格式为hello java t
  • 队列的简单示例

    1 xff0c 队列的简单应用 热土豆问题 span class token keyword from span pythonds span class token punctuation span basic span class tok
  • Git使用经验指南小结

    在使用git的时候 xff0c 每次都要查询需要的命令 xff0c 费时费力 xff0c 在这里简单总结下容易遗忘的点与命令行 xff1a 1 xff0c 首先需要安装git 安装完成后 xff0c 通过以下指令查看git版本 span c
  • 正版matlab安装详解——基于linux服务器平台正版非镜像安装

    想要在学校服务器上安装matlab 但是搜索了半天 xff0c 没有发现什么详细的攻略 xff0c 先将其总结如下 xff1a 1 在根据当前服务器环境选择合适的matlab后 xff0c 上传至服务器文件夹 xff1b 并完成解压 xff
  • 【疑难杂症】Ubuntu安装uWsgi出现的问题

    在Ubuntu环境下安装uWsgi的时候出现了一些问题 1 xff1a 安装时出现错误 xff1a x86 64 linux gnu gcc pthread plugins python python plugin o In file in
  • 基于Hexo框架快速搭建个人博客--搭建(一)

    基于Hexo框架快速搭建个人博客 搭建 xff08 一 xff09 一 HEXO框架二 安装Node js三 安装Git四 安装Hexo五 设置主题六 本地发布文章七 总结 博客链接 xff1a 会思想的苇草i文章链接 xff1a 基于He
  • 安装UR5功能包(翻译)

    翻译地址 由于本人能力有限 xff0c 难免存在模糊或错误之处 xff0c 希望见谅和指正 如果能够对你有点帮助 xff0c 我会感到荣幸 安装 有两种方法用来安装UR5功能包 第一种是直接使用二进制包来安装 xff0c 第二种是在catk
  • 李飞飞发表研究新成果:视觉推理的推断和执行程序(HR)

    原文 论文导读 xff1a 目前进行视觉推理的方法都是通过黑箱结构将输入直接映射到输出 xff0c 而不是对潜在的推理过程进行明确建模 这样一来 xff0c 黑箱模型学习到的是利用数据内的偏置而不是学习进行视觉推理的过程 受到模块化网络的启
  • 构建Linux Samba支持任意WIN10访问(无需改策略)

    传统方式构建的Linux Samba无法直接被WIN10访问 xff0c 大多需要在要访问的WIN10系统上改变组策略 这个方法虽然可行 xff0c 但是大量WIN10系统的组策略修改较为繁琐 之所以WIN10无法访问是当SAMBA连接开始
  • VNC SERVER 安装

    1 用root用户身份运行以下命令 yum install tigervnc server 2 停用防火墙 systemctl stop firewalld service systemctl disable firewalld servi
  • Ubuntu 更换清华大学镜像源

    Ubuntu 更换镜像源 通常我们使用ubunntu的时候总是出现网络过慢导致的更新下载失败等问题 Ubuntu默认的服务器是在国外 xff0c 自然连接就很慢 这里我们更换成国内的镜像源 xff0c 这里使用清华镜像源 操作步骤如下 xf
  • C语言strtok函数的用法

    先理解strtok函数的定义 xff0c 尤其是指针方面的 xff0c 需要自己理解 原型 xff1a char strtok char s const char delim include lt string h gt 分解字符串为一组字
  • ubuntu mate18.04+树莓派4B+ROS安装详细教程

    前记 最近项目需要 xff0c 需要给树莓派4B 安装Ubuntu mate xff0c 本来是一件很简单的事情 xff0c 因为Ubuntu mate官网已经开始支持树莓派4B了 xff0c 但是实际操作后 xff0c 才发现烧录官方的桌
  • FreeRTOS可视化追踪软件 —— 破解Tracealyzer 4.2.12

    方法一 愚人节破解Tracealyzer 4 2 12 xff08 若发这里不妥 xff0c 可通知删贴 xff09 http www stmcu org cn module forum thread 620069 1 1 html 4 3
  • tensorflow2(GPU)显卡版安装

    准备工作 硬件 xff1a 一张算力3 5以上的NVIDIA显卡 查询链接 link 软件 xff1a Miniconda3 pycharm NVIDIA显卡驱动 30系列以前 xff1a cuda 10 1 cudnn 10 1 v7 6
  • elasticsearch底层引擎替换之索引创建+文档添加

    最近在改elasticsearch的源码 xff0c 真的蛋疼 xff0c 现在先记录一下遇到的问题 首先 xff0c 我们在做的是替换掉elasticsearch的底层引擎 xff0c 也就是把lucene替换成我们自己的引擎 这个工作起
  • Winform 集成零散dll进exe的方法

    Winform程序经常需要引用一些第三方控件 xff0c 这些控件大多以DLL的形式提供 另外 xff0c 一般USB桥芯片的官方提供 net操作类库也都是DLL形式提供的 因此一个稍大的项目中往往有一大堆的零散的DLL文件 xff0c 而
  • vncserver 使用遇到的问题

    今天使用vncserver遇到了几个问题 xff0c 如下 xff1a 1 使用普通账户无法修改该账户下的vncpasswd xff1a 解决方法 xff1a 打开 vnc目录 xff0c ls l看一下发现 passwd这个文件的用户和用
  • slf4j的MDC对象和ThreadLocal简单分析

    MDC xff08 Mapped Diagnostic Context xff0c 映射调试上下文 xff09 是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能 某些应用程序采用多线程的方式来处理多个用户的请求