美文网首页
log4j中MDC使用陷阱

log4j中MDC使用陷阱

作者: 全都是泡沫啦 | 来源:发表于2018-11-28 18:10 被阅读0次

1. 背景

同学求助,生产环境对于方法JSONObject.toJSONString()时不时的报以下错误,一看这个大家都会知道HashMap中modCount发生了变化,与初始化生成迭代器HashIterator的expectedModCount不同,那modCount发生变化的场景:clear(),putVal(),removeNode(),computeIfAbsent(),compute(),merge().一定是在遍历过程发生的HashMap增删变化,这个毋庸置疑。接下来我看实际场景

Exception in thread "pool-1-thread-2" java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429)
    at java.util.HashMap$EntryIterator.next(HashMap.java:1463)
    at java.util.HashMap$EntryIterator.next(HashMap.java:1461)
    at com.alibaba.fastjson.serializer.MapSerializer.write(MapSerializer.java:79)
    at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:361)
    at com.alibaba.fastjson.JSON.toJSONString(JSON.java:594)
    at TestThread.lambda$main$1(TestThread.java:33)
  1. 对问题代码进行了抽取, 如下
<dependencies>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.3</version>
        </dependency>
    </dependencies>
public class TestThread {
    public static JSONObject getJSONObject() {
        JSONObject json = (JSONObject) MDC.get("json");
        if (json == null) {
            json = new JSONObject();
            MDC.put("json", json);
        }
        jsonObject.clear();
        return json;
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        boolean flag = true;
        if (flag) {
            final JSONObject jsonObject = getJSONObject();
            executorService.execute(() -> {
                System.out.println("父线程json与子线程json是否相等:" + (jsonObject == getJSONObject()));
            });
        }
        executorService.execute(() -> {
            while (true) {
                getJSONObject().toJSONString();
            }
        });
        executorService.execute(() -> {
            while (true) {
                JSONObject jsonObject1 = getJSONObject();
                while (true) {
                    jsonObject1.put("K_" + System.currentTimeMillis(), null);
                }
            }
        });
    }
}
  1. 运行结果
当flag=true
父线程json与子线程json是否相等:true
Exception in thread "pool-1-thread-2" java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429)
    at java.util.HashMap$EntryIterator.next(HashMap.java:1463)
    at java.util.HashMap$EntryIterator.next(HashMap.java:1461)
    at com.alibaba.fastjson.serializer.MapSerializer.write(MapSerializer.java:79)
    at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:361)
    at com.alibaba.fastjson.JSON.toJSONString(JSON.java:594)
    at TestThread.lambda$main$1(TestThread.java:33)
    at TestThread$$Lambda$2/457233904.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

当flag=false
无报错结果
  1. 分析定位到org.apache.log4j.MDC问题:错误使用InheritableThreadLocal
  2. InheritableThreadLocal子线程会copy父线程中的所有ThreadLocal的变量,对于引用类型JSONObject则导致发现并发操作问题
  3. 解决方法
private static final ThreadLocal<JSONObject>  JSON_OBJECT_THREAD_LOCAL= new ThreadLocal();

    public static JSONObject getJSONObject(){
        JSONObject jsonObject = JSON_OBJECT_THREAD_LOCAL.get();
        if (jsonObject == null){
            jsonObject = new JSONObject();
            JSON_OBJECT_THREAD_LOCAL.set(jsonObject);
        }
        jsonObject.clear();
        return jsonObject;
    }

相关文章

  • log4j中MDC使用陷阱

    1. 背景 同学求助,生产环境对于方法JSONObject.toJSONString()时不时的报以下错误,一看这...

  • Java日志Log4j或者Logback的NDC和MDC功能

    NDC和MDC的区别 Java中使用的日志的实现框架有很多种,常用的log4j和logback以及java.uti...

  • log4j配置文档

    Log4j简介 通过使用Log4j可以看到程序运行过程中更详细的信息经常在系统之使用Log4j查看日志 使用方法 ...

  • SpringBoot+MDC实现全链路调用日志跟踪,排查问题更方

    写在前面 通过本文将了解到什么是MDC、MDC应用中存在的问题、如何解决存在的问题 MDC介绍 简介: MDC(M...

  • log4j

    .log4j介绍 1通过log4j可以看到程序运行过程中更详细的信息 (1)经常使用log4j查看日志 2使用 (...

  • java 注解结合 spring aop 实现日志traceId

    MDC 的必要性 日志框架 日志框架成熟的也比较多: slf4j log4j logback log4j2 我们没...

  • logback的MDC机制

    logback的MDC机制 1. MDC 介绍: ​ MDC(Mapped Diagnostic Contex...

  • log4j

    1、通过log4j可以看到程序运行过程中的更详细信息 经过log4j查看日志 2、使用 导入log4j的jar包i...

  • 2017-10-26 Slf4j MDC 使用和 基于 Logb

    Slf4j MDC 使用和 基于 Logback 的实现分析 今天就转载一篇 日志MDC的文章,作者非常用心,写的...

  • logback扩展字段

    利用logback的MDC进行扩展 文档 MDC类: 目的: 在elk系统中,Kibana作展示的时候,要实现右侧...

网友评论

      本文标题:log4j中MDC使用陷阱

      本文链接:https://www.haomeiwen.com/subject/idjzqqtx.html