Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

ConcurrentModificationException iterating

7 views
Skip to first unread message

Alex Magro

unread,
Jun 10, 2005, 11:44:55 AM6/10/05
to
Hi,

ich bekomme die folgende Fehlermeldung:

java.util.ConcurrentModificationException
at java.util.TreeMap$EntryIterator.nextEntry(Unknown Source)
at java.util.TreeMap$KeyIterator.next(Unknown Source)
at simulator.TimeCounterThread.run(TimeCounterThread.java:51)

Ich weiß prinzipiell auch schon woran die liegt: Ich habe eine Klasse
die als Datenelement folgendes besitzt:
private Map nodeList = Collections.synchronizedMap(new TreeMap());

Nun existieren 2 Threads... der eine Thread fügt ständig neue Elemente
in diese Liste ein, der andere Thread greift alle Skeunde auf die Liste
zu und durchläuft sie... d.h. ich verändere die Liste während ich über
sie iteriere.

Das ist böse... ich weiß ;). Ich habe nun auch in der API gelesen, dass
sowas nicht geht, wegen fail-fast. Das Problem ist nur, wie kann ich
diesen Fehler beheben... im Prunzip würde es mir ja schon reichen, jede
Sekunde eine Kopie von der TreeMap anzufertigen und über diese dann zu
iterieren... nur dummerweise gibt es kein Clone in Map und mit new
funktioniert es auch nicht, da erhalte ich die gleiche Exeption. Gibt es
für mein Problem irgendeine einfache Lösung?

Grüße,
Alex

Marco Lange

unread,
Jun 10, 2005, 12:18:32 PM6/10/05
to
Hallo Alex,

Dir fehlt die richtige Synchronisation. Zwar verwendest Du schon
Collections.syncronizedMap(...), um Deine Map zu wrappen, jedoch löst Du
damit nicht das Problem der Concurrent Modification, da ja der Iterator
nicht mit den Schreibzugriffen synchronisiert wird.

Daher bräuchtest Du eigentlich eine Synchronisation während der Iterator
läuft, so heißen, etwa folgendes:

Object iteratorLock = new Object();
TreeMap map = new TreeMap();
...

Thread 1:
synchronized (iteratorLock) {
// Elemente hinzufügen
map.put(...,...);
...
}

Thread 2:
synchronized (iteratorLock) {
for (Iterator iterator = map.keySet().iterator;
iterator.hasNext(); ) {
....
}
}

Alternativ kannst Du unter JDK 5.0 auch ein Lock bzw. ReadWriteLock
verwenden.

Je nachdem, wie lange das Hinzufügen oder Iterieren dauert, kann das
natürlich durch die Synchronisation langsamer werden, das musst Du
ausprobieren. Ansonsten ist vielleicht doch kopieren angesagt (welches
aber auch synchronisiert werden sollte). Oder Du musst Dir etwas
aufwändigeres einfallen lassen, etwa ein Zwischenspeicher, wo während
des Iterierens neue Elemente kurz zwischengelagert werden können.

Viele Grüße,
Marco

Frank Dreyer

unread,
Jun 10, 2005, 12:48:36 PM6/10/05
to
Alex Magro schrieb:

> ich bekomme die folgende Fehlermeldung:
>
> java.util.ConcurrentModificationException
> at java.util.TreeMap$EntryIterator.nextEntry(Unknown Source)
> at java.util.TreeMap$KeyIterator.next(Unknown Source)
> at simulator.TimeCounterThread.run(TimeCounterThread.java:51)
>
> Ich weiß prinzipiell auch schon woran die liegt: Ich habe eine Klasse
> die als Datenelement folgendes besitzt:
> private Map nodeList = Collections.synchronizedMap(new TreeMap());
>
> Nun existieren 2 Threads... der eine Thread fügt ständig neue Elemente
> in diese Liste ein, der andere Thread greift alle Skeunde auf die Liste
> zu und durchläuft sie... d.h. ich verändere die Liste während ich über
> sie iteriere.
>
> Das ist böse... ich weiß ;). Ich habe nun auch in der API gelesen, dass
> sowas nicht geht, wegen fail-fast. Das Problem ist nur, wie kann ich
> diesen Fehler beheben...

In der Dokumentation zu Collections.synchronizedFoo steht dazu ein
Code-Beispiel (synchronized-Block um die Iterator-Nutzung legen)

> im Prunzip würde es mir ja schon reichen, jede
> Sekunde eine Kopie von der TreeMap anzufertigen und über diese dann zu
> iterieren... nur dummerweise gibt es kein Clone in Map und mit new
> funktioniert es auch nicht, da erhalte ich die gleiche Exeption. Gibt es
> für mein Problem irgendeine einfache Lösung?

Da die TreeMap(Map) und TreeMap(SortedMap) Konstruktoren intern einen
Iterator verwenden, musst du den Konstruktor-Aufruf ebenfalls in einen
synchronized-Block legen.

Alex Magro

unread,
Jun 10, 2005, 4:50:28 PM6/10/05
to
Hi,

Da der Iterations Thread die ganze Zeit läuft, d.h. es wird ständig
durch die Liste iteriert, wird wohl das kopieren das beste sein, denke
ich, oder?

Frank Fiedler

unread,
Jun 10, 2005, 5:15:39 PM6/10/05
to
Alex Magro wrote:

> Nun existieren 2 Threads... der eine Thread fügt ständig neue Elemente
> in diese Liste ein, der andere Thread greift alle Skeunde auf die Liste
> zu und durchläuft sie... d.h. ich verändere die Liste während ich über
> sie iteriere.

Ich habe das mal so gelöst, dass ich das Hinzufügen nicht direkt mache,
sondern die hinzuzufügenden Objekte in eine Queue packe. Diese wird dann
von dem Thread der durchiteriert abgearbeitet, wenn er mit iterieren
fertig ist. Geht nicht in jeder Situation,ist aber auf jeden Fall
einfach. :-)

gruss
Frank

--
e-mail : fiedler.bass<at>web.de

Alex Magro

unread,
Jun 11, 2005, 6:33:44 AM6/11/05
to
Hi,

> Ich habe das mal so gelöst, dass ich das Hinzufügen nicht direkt mache,
> sondern die hinzuzufügenden Objekte in eine Queue packe. Diese wird dann
> von dem Thread der durchiteriert abgearbeitet, wenn er mit iterieren
> fertig ist. Geht nicht in jeder Situation,ist aber auf jeden Fall
> einfach. :-)

Das geht in meinem Fall, glaube ich zumindest, nicht, da es sich um ein
ständiges iterieren handelt.

Im Prinzip entwickele ich einen Simulator, der u.a. eine Zeitkomponente
und Zufallskomponente besitzt. Da ich nicht so gerne mit realen Sekunden
arbeiten möchte, habe ich einen Thread, der diese "Sekunden" sehr
schnell simuliert.

Da in meiner Simulation die Zeit sich ständig verringert (auch wenn es
eine Sekunge am Anfang ungleich einer Sekunde nach 2 Stunden ist), kann
ich sowas wie von Dir oben beschrieben nicht benutzen, oder?

Grüße,
Alex

Frank Fiedler

unread,
Jun 11, 2005, 10:41:29 AM6/11/05
to
Alex Magro wrote:

> Das geht in meinem Fall, glaube ich zumindest, nicht, da es sich um ein
> ständiges iterieren handelt.

Aber irgendwann hast du doch durchiteriert und setzt neu an,oder? Da
müsste dann das einfügen stattfinden, so hatte ich das gemeint.

> Im Prinzip entwickele ich einen Simulator, der u.a. eine Zeitkomponente
> und Zufallskomponente besitzt. Da ich nicht so gerne mit realen Sekunden
> arbeiten möchte, habe ich einen Thread, der diese "Sekunden" sehr
> schnell simuliert.
>
> Da in meiner Simulation die Zeit sich ständig verringert (auch wenn es
> eine Sekunge am Anfang ungleich einer Sekunde nach 2 Stunden ist), kann
> ich sowas wie von Dir oben beschrieben nicht benutzen, oder?

Ich bin nicht sicher,ob ich verstanden habe, was du machst, aber als
weitere Idee hätte ich dazu höchstens noch, die Datenstruktur die du
brauchst,selber zu bauen,und dann ist dein Iterator eben fail-never;-)
Habe aber keine Vorstellung, welche Probleme sich daraus möglicherweise
ergeben, da kommt es dann sicher ganz genau drauf an, wofürdu es nimmst.

gruss
ff

Paul Ebermann

unread,
Jun 11, 2005, 8:52:48 AM6/11/05
to
"Alex Magro" skribis:

> > Ich habe das mal so gelöst, dass ich das Hinzufügen nicht direkt mache,
> > sondern die hinzuzufügenden Objekte in eine Queue packe. Diese wird dann
> > von dem Thread der durchiteriert abgearbeitet, wenn er mit iterieren
> > fertig ist. Geht nicht in jeder Situation,ist aber auf jeden Fall
> > einfach. :-)
>
> Das geht in meinem Fall, glaube ich zumindest, nicht, da es sich um ein
> ständiges iterieren handelt.

Das Iterieren fängt doch irgendwann an, und hört
irgendwann auf (und geht dann neu los), oder?

> Im Prinzip entwickele ich einen Simulator, der u.a. eine Zeitkomponente
> und Zufallskomponente besitzt. Da ich nicht so gerne mit realen Sekunden
> arbeiten möchte, habe ich einen Thread, der diese "Sekunden" sehr
> schnell simuliert.
>
> Da in meiner Simulation die Zeit sich ständig verringert (auch wenn es
> eine Sekunge am Anfang ungleich einer Sekunde nach 2 Stunden ist), kann
> ich sowas wie von Dir oben beschrieben nicht benutzen, oder?

Mir ist nicht ganz klar, was die Zeit mit der
Iteration zu tun hat.

Ich würde sagen, für deinen Zweck ist die TreeMap nicht
gemacht - aber wenn du etwas genauer beschreibst, was du
eigentlich machen willst, können wir dir bestimmt besser
helfen.


Paul
--
Zitieren im Usenet: http://einklich.net/usenet/zitier.htm
Warum Realnamen: http://www.wschmidhuber.de/realname/

Marco Lange

unread,
Jun 11, 2005, 2:18:55 PM6/11/05
to
Hi,

> Da der Iterations Thread die ganze Zeit läuft, d.h. es wird ständig
> durch die Liste iteriert, wird wohl das kopieren das beste sein, denke
> ich, oder?

Hmmm, in Deinem Originalposting schriebst Du, dass der Thread jede
Sekunde durch die Map (wieso plötzlich Liste?) iteriert. Wenn die Liste
und die darauf basierenden Berechnungen nicht gerade sehr aufwändig
sind, sollte das Iterieren ja nicht so lange dauern.

Wieviele Elemente sind so im Schnitt in der Map?

Viele Grüße,
Marco

Alex Magro

unread,
Jun 12, 2005, 7:48:29 AM6/12/05
to
Hi,

Marco Lange wrote:
> Hmmm, in Deinem Originalposting schriebst Du, dass der Thread jede
> Sekunde durch die Map (wieso plötzlich Liste?) iteriert. Wenn die Liste
> und die darauf basierenden Berechnungen nicht gerade sehr aufwändig
> sind, sollte das Iterieren ja nicht so lange dauern.
>
> Wieviele Elemente sind so im Schnitt in der Map?

Man kann nicht so genau sagen, wieviele Elemente "im Schnitt" in der Map
sind. Anfänglich sind viellleicht nur 10.000 Elemente drin,
letztendliches Ziel (so vielleicht nach 24h Laufzeit) sind ca. 3-5 Mio.

Viele Grüße,
Alex

Alex Magro

unread,
Jun 12, 2005, 8:00:46 AM6/12/05
to
Hi,

> Das Iterieren fängt doch irgendwann an, und hört
> irgendwann auf (und geht dann neu los), oder?

Das stimmt.

> Mir ist nicht ganz klar, was die Zeit mit der
> Iteration zu tun hat.

Die Iteration läuft fertig, dann wird eine Sekunde gewartet und dann
wird die nächste Iteration gestartet.

> Ich würde sagen, für deinen Zweck ist die TreeMap nicht
> gemacht - aber wenn du etwas genauer beschreibst, was du
> eigentlich machen willst, können wir dir bestimmt besser
> helfen.

Wieso ist eine TreeMap nicht geeignet für sowas?

Ich habe einen 3 verschiedene Threads:
1.) Erzeugt neue Elemente und fügt sie in die Map ein (zusätzlich werden
noch zahlreiche andere komplizierte Strukturen aufgebaut, für die ich
u.a. einen schnellen Zugriff über eine ID auf die Elemente der Map
benötige). Dieser Thread ist der eigentlich zeitintensive Teil.
2.) Iteriert durch alle Elemente der Map und löscht in Abhängigkeit von
bestimmten Bedingungen Elemente aus der Liste. Generell ist dieser
Prozess nicht so aufwendig wie der 1.), kann jedoch trotzdem manchmal
weitreichende Umstrukturierungen und Prozesse nach sich ziehen (jedoch
nur selten).
3.) Ein letzter Thread, der alle 30 Sekunden eine Ausgabe über die
aktuellen Ergebnisse der Simulation ausgibt.

Damit die Simulation einigermaßen realistisch arbeitet, sollten 1 und 2
unabhängig voneinander laufen. Damit es wirklich realistisch ist, müsste
eigentliche jedes Element der Map einen eigenen Thread besitzen, bei bis
zu 5 Mio Elementen in der Map ist dies wohl mit meinen Ressourcen nicht
mehr durchführbar.

Viele Grüße,
Alex

Alex Magro

unread,
Jun 12, 2005, 8:42:36 AM6/12/05
to
Hi,

ich hätte noch eine Frage zu den synchronized-Blöcken. Gibt es
eigentlich irgendeine einfache Regel, um welche Teile ein
synchronized-Block gehört? Muss um das durchiterieren genauso ein
synchronized-Block, wie um das Einfügen und Löschen von Elementen aus
einer Map?

Sollte man sicherheitshalber alle Stellen, die auf die Map zugreifen mit
einem synchronized-Block umrunden?

Viele Grüße und danke,
Alex

Paul Ebermann

unread,
Jun 12, 2005, 12:12:36 PM6/12/05
to
"Alex Magro" skribis:

> > Das Iterieren fängt doch irgendwann an, und hört
> > irgendwann auf (und geht dann neu los), oder?
>
> Das stimmt.
>
> > Mir ist nicht ganz klar, was die Zeit mit der
> > Iteration zu tun hat.
>
> Die Iteration läuft fertig, dann wird eine Sekunde gewartet und dann
> wird die nächste Iteration gestartet.

Wenn da mehrere Millionen Elemente in der Map sind,
dauert eine Iteration eine ganze Weile.

Währenddessen kann die TreeMap nicht geändert werden
(dafür ist sie nicht gemacht).

Die Strategie "ich setze alle zwischendurch erstellten
Objekte in eine Warteschlange und füge sie dann auf
einmal ein" passt hier wohl nicht.

> > Ich würde sagen, für deinen Zweck ist die TreeMap nicht
> > gemacht - aber wenn du etwas genauer beschreibst, was du
> > eigentlich machen willst, können wir dir bestimmt besser
> > helfen.
>
> Wieso ist eine TreeMap nicht geeignet für sowas?

Siehe oben - du kannst nur abwechselnd ändern (außer
im Iterator) oder durchgehen, nicht beides gleichzeitig.

> Ich habe einen 3 verschiedene Threads:
> 1.) Erzeugt neue Elemente und fügt sie in die Map ein (zusätzlich werden
> noch zahlreiche andere komplizierte Strukturen aufgebaut, für die ich
> u.a. einen schnellen Zugriff über eine ID auf die Elemente der Map
> benötige). Dieser Thread ist der eigentlich zeitintensive Teil.
>
> 2.) Iteriert durch alle Elemente der Map und löscht in Abhängigkeit von
> bestimmten Bedingungen Elemente aus der Liste. Generell ist dieser
> Prozess nicht so aufwendig wie der 1.), kann jedoch trotzdem manchmal
> weitreichende Umstrukturierungen und Prozesse nach sich ziehen (jedoch
> nur selten).

Muss dieses Iterieren in einer bestimmten Reihenfolge geschehen?
Ist es wesentlich, ob die zwischendurch eingefügten Objekte schon
dabei sind?

> 3.) Ein letzter Thread, der alle 30 Sekunden eine Ausgabe über die
> aktuellen Ergebnisse der Simulation ausgibt.

Greift der auf die Map zu?

> Damit die Simulation einigermaßen realistisch arbeitet, sollten 1 und 2
> unabhängig voneinander laufen. Damit es wirklich realistisch ist, müsste
> eigentliche jedes Element der Map einen eigenen Thread besitzen, bei bis
> zu 5 Mio Elementen in der Map ist dies wohl mit meinen Ressourcen nicht
> mehr durchführbar.

Ja.

So wie ich das sehe, brauchst du eine Datenstruktur, in der
das durchgehen/löschen parallel zum Einfügen passieren kann
(und trotzdem nichts schief geht).

Ich würde da (wenn die Iterations-Reihenfolge nicht
zwingend etwas mit der Map-Zuordnung zu tun haben muss)
eine eigene Hash-basierte struktur basteln (vorher in
der richtigen Größe angelegt, Synchronisation nur lokal
pro bucket). Oder eine eigene Baum-basierte Struktur,
die nur lokal synchronisiert.


Paul
--
Alle meine Antworten, Feststellungen, Behauptungen
sind mit AFAIK und IMHO zu lesen und nicht in der
Luft- und Raumfahrt oder Atomindustrie zu verwenden.

0 new messages