Java 反射 mdc失效,log4j中MDC使用陷阱

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)

对问题代码进行了抽取, 如下

log4j

log4j

1.2.17

com.alibaba

fastjson

1.2.3

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);

}

}

});

}

}

运行结果

当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

无报错结果

分析定位到org.apache.log4j.MDC问题:错误使用InheritableThreadLocal

InheritableThreadLocal子线程会copy父线程中的所有ThreadLocal的变量,对于引用类型JSONObject则导致发现并发操作问题

解决方法

private static final ThreadLocal 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;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值