webentwicklung-frage-antwort-db.com.de

Java-synchronisierte Liste

Ich habe eine vorgefüllte Array-Liste. Und ich habe mehrere Threads, die Elemente aus der Array-Liste entfernen. Jeder Thread ruft die Remove-Methode unten auf und entfernt ein Element aus der Liste. Verhält sich der folgende Code zu einem konsistenten Verhalten?

ArrayList<String> list = Collections.synchronizedList(new ArrayList<String>());

void remove(String item)
{
     do something; (doesn't work on the list)
     list.remove(item);
}

Vielen Dank!

60
shadowfax

Ja, seien Sie vorsichtig, wenn Sie auch über die Liste iterieren, denn in diesem Fall müssen Sie darauf synchronisieren. Aus dem Javadoc :

Es ist zwingend erforderlich, dass der Benutzer in der zurückgegebenen Liste manuell synchronisiert, wenn er wiederholt wird:

List list = Collections.synchronizedList(new ArrayList());
    ...
synchronized (list) {
    Iterator i = list.iterator(); // Must be in synchronized block
    while (i.hasNext())
        foo(i.next());
}

Oder Sie können CopyOnWriteArrayList verwenden, das für das Schreiben langsamer ist, aber dieses Problem nicht aufweist.

59
Peter Lawrey

Das sollte in Ordnung sein, solange die "remove" -Methode nicht atomar ist.

Mit anderen Worten, wenn "etwas tun" überprüft wird, dass das Element beispielsweise mehr als einmal in der Liste erscheint, ist das Ergebnis dieser Prüfung möglicherweise ab dem Zeitpunkt, zu dem Sie die nächste Zeile erreichen, falsch.

Stellen Sie außerdem sicher, dass Sie in der Liste synchronisieren, wenn Sie iterieren:

synchronized(list) {
    for (Object o : list) {}
}

Wie von Peter Lawrey erwähnt, kann CopyOnWriteArrayList Ihnen das Leben erleichtern und in einer sehr parallelen Umgebung eine bessere Leistung erbringen.

23
assylias

Aus Collections#synchronizedList(List) javadoc

Gibt eine synchronisierte (Thread-sichere) Liste zurück, die vom angegebenen .__ gesichert wird. Liste. Um den seriellen Zugriff zu gewährleisten, ist es wichtig, dass all Zugriff auf die Backing-Liste wird durchgeführt durch die zurückgegebene Liste ... Es ist zwingend erforderlich, dass der Benutzer die manuell zurückgelieferte Liste synchronisiert, wenn er wiederholt wird. Die Nichtbeachtung dieses Hinweises kann zu nicht deterministischem Verhalten führen.

11
gkuzmin

Sie können zwei verschiedene Probleme mit Listen haben:
1) Wenn Sie eine Änderung innerhalb einer Iteration vornehmen, obwohl in einer Mono-Thread-Umgebung, gibt es eine ConcurrentModificationException wie in dem folgenden Beispiel:

List<String> list = new ArrayList<String>();
for (int i=0;i<5;i++)
   list.add("Hello "+i);

for(String msg:list)
   list.remove(msg);

Um dieses Problem zu vermeiden, können Sie Folgendes tun:

for(int i=list.size()-1;i>=0;i--)
   list.remove(i);

2) Das zweite Problem könnte die Multi-Threading-Umgebung sein. Wie oben erwähnt, können Sie synchronisierte (Liste) verwenden, um Ausnahmen zu vermeiden.

2
gezdy

Es wird ein konsistentes Verhalten für das Hinzufügen/Entfernen von Operationen erhalten. Beim Iterieren müssen Sie jedoch explizit synchronisieren. Siehe diesen Link

0
Stauz

Ja, es wird gut funktionieren, da Sie synchronized die Liste haben. Ich würde vorschlagen, dass Sie CopyOnWriteArrayList verwenden.

CopyOnWriteArrayList<String> cpList=new CopyOnWriteArrayList<String>(new ArrayList<String>());

    void remove(String item)
    {
         do something; (doesn't work on the list)
                 cpList..remove(item);
    }
0
amicngh