记一次异常 JSONObject toString 出现并发修改

2019-08-18  本文已影响0人  古都旧城

场景描述

做数据上报组件的时候,把jsonObject对象当成了一个方法的参数,由外层传入,而操作此对象的线程可能有多个,比如:上报线程、备份线程等等,然后当数据量大的时候,偶发并发修改异常,经常发现在jsonObject.toString()的时候。
异常

08-13 15:10:54.207 16625-16709/com..xxx E/Tinker.UncaughtHandler: TinkerUncaughtHandler catch exception:java.util.ConcurrentModificationException
        at java.util.LinkedHashMap$LinkedHashIterator.nextEntry(LinkedHashMap.java:346)
        at java.util.LinkedHashMap$EntryIterator.next(LinkedHashMap.java:375)
        at java.util.LinkedHashMap$EntryIterator.next(LinkedHashMap.java:373)
        at org.json.JSONObject.writeTo(JSONObject.java:719)
        at org.json.JSONStringer.value(JSONStringer.java:237)
        at org.json.JSONObject.writeTo(JSONObject.java:720)
        at org.json.JSONStringer.value(JSONStringer.java:237)
        at org.json.JSONArray.writeTo(JSONArray.java:613)
        at org.json.JSONArray.toString(JSONArray.java:585)
        at com.xxx.xxx.report.thread.CommonBackupThread.innerBackup(CommonBackupThread.java:36)
        at com.xxx.xxx.report.thread.CommonBackupThread.access$000(CommonBackupThread.java:22)
        at com.xxx.xxx.report.thread.CommonBackupThread$1.run(CommonBackupThread.java:49)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
        at java.lang.Thread.run(Thread.java:818)

从上面可以看出,我们在调用JSONArray对象的toString()方法的时候抛出了并发修改异常。

问题分析

什么是并发修改异常
这个异常一般我们还都比较熟悉,大多数场景一般出现在对集合的操作上边:一边迭代一边对集合进行修改,就很容易发生并发修改异常。

例如下面这个实例就会出现并发修改异常

      );
        list.add(2);
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()) {
            Integer s = iterator.next();
            if (s == 2) {
                list.remove(s);
            }

        }

具体抛出并发异常的代码分析可以参见:https://www.cnblogs.com/bsjl/p/7676209.html

我们主要分析为何jsonObject 或者 jsonArray toString会抛出异常
首先看jsonObject的toString 方法

    @Override public String toString() {
        try {
            JSONStringer stringer = new JSONStringer();
            writeTo(stringer);
            return stringer.toString();
        } catch (JSONException e) {
            return null;
        }
    }

进入writeTo 方法,可以看到是有一个map集合nameValuePairs的遍历

   void writeTo(JSONStringer stringer) throws JSONException {
        stringer.object();
        for (Map.Entry<String, Object> entry : nameValuePairs.entrySet()) {
            stringer.key(entry.getKey()).value(entry.getValue());
        }
        stringer.endObject();
    }

然后在我们像jsonObject塞入数据的时候是这样的

   public JSONObject put(String name, boolean value) throws JSONException {
        nameValuePairs.put(checkName(name), value);
        return this;
    }

看到这里结论就很明确了,toString有调用集合的高级for循环(底层迭代器实现的),然后添加数据的时候又会直接操作集合,读写又不在同一个线程,自然就容易出问题。

异常发生原因回顾

总结

上一篇下一篇

猜你喜欢

热点阅读