一度異常JSONObject toStringが発生した同時修正異常C o n c u r r e ntModificationExceptionを記録する

3854 ワード

シーンの説明


データを報告するコンポーネントを作る時、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 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 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ループ(下位反復器で実現される)があり,データを追加する際に直接集合を操作し,読み書きが同じスレッドにないため,自然と問題が発生しやすい.
異常発生原因の回顧
  • 1、外層は、報告されたデータを1つのjsonObjectにカプセル化して、報告コンポーネント(コンポーネント内に配布、バックアップなどのスレッドが含まれる)
  • に伝達する.
  • 2、コンポーネント内のバックアップスレッドは、jsonObjectのtoString()メソッドを呼び出し、このメソッドは集合遍歴を含む.
  • 3、これと同時に外層は依然としてこのオブジェクトを修正して他の用途として使用し、オブジェクトの修正も集合の修正に関連している.
  • 上の2つの動作は、同じスレッドではなく、異なるスレッドが同時に動作し、集合の同時修正異常がトリガーされ、クラッシュを引き起こす.

  • まとめ

  • マルチスレッドは、どのオブジェクトを操作しても問題になりやすい.jsonObjectだけでなく、オブジェクトに読み書きが同時に存在する可能性がある場合は、パラメータとして複数のスレッド間を往復するべきではない.これは安全ではなく、低レベルのエラーでもある.
  • 修正案:ここでシーンはレポートコンポーネントのパラメータであり、ロックは明らかに不合理である.ここでは、複数のシーンで使用されているオブジェクトが同じではないことを保証しなければならない(もちろん、具体的なシーンの具体的な分析が必要である.ここではレポートデータであり、データが転送された後、外層がこのオブジェクトをどのように変更したかに関心を持つ必要はないので、新しいオブジェクトを作成することでこのような状況を回避することができる).