3

私が得ているエラーを理解するのを手伝ってください:

private void replayHistory() {
    synchronized (alarmsHistory) {
        for (AlarmEvent alarmEvent : alarmsHistory) {
            LOG.error("replayHistory " + alarmEvent.type + " " + alarmEvent.source);
            sendNotification(alarmEvent.type, alarmEvent.source, alarmEvent.description, 
                    alarmEvent.clearOnOtherStations, alarmEvent.forceClearOnOtherStations);         
        }
    }
}

それに要素を追加するメソッド

private void addToAlarmsHistory(AlarmEvent alarmEvent) {
    synchronized (alarmsHistory) {
        LOG.error("addToAlarmsHistory " + alarmEvent.type + " " + alarmEvent.source);
        alarmsHistory.add(alarmEvent);
    }
}

メソッドと Set の両方

private volatile Set<AlarmEvent> alarmsHistory = new LinkedHashSet<AlarmEvent>();

で定義されています

JmxGwReloadThread extends Thread class

これは内部クラスです

AlarmManager class

それはメソッドを持っています

private void addToReplayHistory(AlarmEvent alarmEvent) {
    if ((jmxThread != null) && (jmxThread.isAlive())) {
        jmxThread.addToAlarmsHistory(alarmEvent);
    }
}

異なるインターフェイスによって呼び出されています (いつ、どのくらいの頻度で呼び出されるかは保証できません)

ある時点で JmxThread が開始され、replayHistory メソッドが呼び出されます

java.util.ConcurrentModificationExceptionがスローされ、ルートは

for (AlarmEvent alarmEvent : alarmsHistory) {

コードはおそらくalarmsHistoryに要素を追加しようとし、interatorの場合

java.util.ConcurrentModificationException
    at java.util.LinkedHashMap$LinkedHashIterator.nextEntry(LinkedHashMap.java:390)
    at java.util.LinkedHashMap$KeyIterator.next(LinkedHashMap.java:401)
    at AlarmManager$JmxGwReloadThread.replayHistory(AlarmManager.java:568)
    at AlarmManager$JmxGwReloadThread.run(AlarmManager.java:532)

nextEntry を呼び出すと例外がスローされますが、同期はそのような問題を防ぐべきではありませんか?

ログは、同期が機能しないことを示しています - replayHistory はそのすべての要素を反復する必要があります (複数の単一の HEARTBEAT_INFO FM を保証できます) が、addToReplayHistory 呼び出しによって中断されます。

2013-07-11 11:58:33,951 Thread-280 ERROR AlarmManager$JmxGwReloadThread.replayHistory(AlarmManager.java:570)  - replayHistory HEARTBEAT_INFO FM
2013-07-11 11:58:33,951 Thread-280 ERROR AlarmManager$JmxGwReloadThread.addToAlarmsHistory(AlarmManager.java:550)  -  addToAlarmsHistory HEARTBEAT_INFO FM
2013-07-11 11:58:33,952 Thread-280 ERROR Log4jConfigurator$UncaughtExceptionHandler.uncaughtException(Log4jConfigurator.java:253)  - Detected uncaught exception in thread: Thread-280
4

4 に答える 4

2

1 つのスレッドが繰り返され、別のスレッドが追加されると、気が狂います。

コードが 2 つの関連するコード ブロックへのアクセスを同期しているように見える場合、alarmsHistory に追加/削除する他の非同期コードを探します。

于 2013-07-11T09:02:17.210 に答える
0

カンの答えにいくつかの詳細を追加するには:

Javaのsynchronizedブロックは再入可能です:

再入可能同期

スレッドは、別のスレッドが所有するロックを取得できないことを思い出してください。ただし、スレッドは既に所有しているロックを取得できます。スレッドが同じロックを複数回取得できるようにすると、再入可能な同期が可能になります。これは、同期されたコードが直接的または間接的に、同期されたコードも含むメソッドを呼び出し、両方のコード セットが同じロックを使用する状況を表します。再入可能な同期がなければ、同期されたコードは、スレッド自体がブロックされるのを避けるために、さらに多くの予防策を講じる必要があります。

kan が指摘したように、実際には、複数のスレッドがコレクションを変更しているわけではなく、再入可能動作のために常にロックを取得できるスレッドは 1 つだけである可能性があります。

例外を修正するために探す必要があるのは、同期ブロック間の再帰呼び出し、または への非同期アクセスalarmsHistoryです。

ConcurrentSkipListCopyOnWriteArraySetなどの並行コレクションを調べることもできます。どちらも例外を防ぐ必要がありますが、JavaDoc に記載されている動作とパフォーマンス特性に注意してください。

于 2013-07-12T10:08:52.647 に答える