Müssen Sie die synchronisierte Liste während der Iteration manuell synchronisieren, wenn sie vermieden werden kann?

7

Meine Frage bezieht sich auf die Collection-Klasse "synchronizedList".

Javadocs sagen:

It is imperative that the user manually synchronize on the returned list when iterating over it:

%Vor%

Obwohl für andere Methoden keine manuelle Synchronisierung erforderlich ist. Ich habe mir den Quellcode der Collections-Klasse angesehen und gefunden shyncronization wurde bereits für alle Methoden wie add

gesorgt %Vor%

aber nicht für die Iterator-Methode . Ich denke, dass die Iterator-Methode auch auf die gleiche Art und Weise mit der Synchronisation umgehen konnte wie oben beschrieben (es hätte die zusätzliche Arbeit vermieden, d. h. manuelle Synchronisation für Programmierer). da bin ich mir sicher muss ein konkreter Grund dafür sein, aber ich vermisse es?

%Vor%

Eine Möglichkeit, die manuelle Synchronisierung von Programmer zu vermeiden

%Vor%     
M Sach 18.09.2013, 13:27
quelle

3 Antworten

16
  

Ich denke, dass die Iterator-Methode die Synchronisation auch auf die gleiche Weise wie die obige Methode behandelt haben könnte

Nein, konnte es absolut nicht.

Der Iterator hat keine Kontrolle darüber, was Ihr Code zwischen Aufrufen der einzelnen Methoden tut. Das ist der Punkt. Ihr Iterationscode ruft hasNext() und next() wiederholt auf, und die Synchronisation während dieser Aufrufe ist zwar möglich, aber irrelevant - wichtig ist, dass kein anderer Code versucht, die Liste über die gesamte Zeit zu ändern du wiederholst .

Stellen Sie sich eine Zeitleiste vor:

%Vor%

Der Iterator kann nicht zwischen dem Ende des Aufrufs von next() bei t = 2 und dem Aufruf von hasNext() bei t = 10 synchronisieren. Also, wenn ein anderer Thread versucht (sagen wir mal), ein Objekt zur Liste bei t = 7 hinzuzufügen, wie soll der Iterator das verhindern?

Dies ist das allgemeine Problem bei synchronisierten Sammlungen: Jede einzelne Operation ist synchronisiert, während Sie normalerweise eine ganze Chunky-Operation synchronisieren möchten.

    
Jon Skeet 18.09.2013, 13:29
quelle
3

Wenn Sie nicht die gesamte Iteration synchronisieren, kann ein anderer Thread die Auflistung während der Iteration ändern, was zu einer ConccurentModificationException führt.

Außerdem ist der zurückgegebene Iterator nicht Thread-sicher.
Sie könnten das beheben, indem Sie den Iterator in eine SynchronizedIterator einschließen, die jede Methode im Iterator sperrt, aber das würde auch nicht helfen - ein anderer Thread könnte die Sammlung zwischen zwei Iterationen noch ändern und alles abbrechen.

Dies ist einer der Gründe, warum die Methoden Collections.synchronized*() völlig nutzlos sind.
Weitere Informationen zur ordnungsgemäßen threadsicheren Sammlung finden Sie unter in meinem Blog .

    
SLaks 18.09.2013 13:28
quelle
0

Wenn Sie die manuelle Serialisierung vermeiden möchten, müssen Sie eine Sammlung wie java.util.concurrent.CopyOnWriteArrayList verwenden. Jedes Mal, wenn ein Objekt zur Liste hinzugefügt wird, wird die zugrunde liegende Datenstruktur kopiert, um eine Ausnahme für gleichzeitige Änderungen zu vermeiden.

Der Grund, warum Sie in Ihrem Beispiel die manuelle Serialisierung am Iterator benötigen, ist, dass der Iterator die gleiche interne Datenstruktur wie die Liste verwendet, aber unabhängige Objekte sind und Iterator und Liste von verschiedenen Threads zu jedem beliebigen Zeitpunkt aufgerufen werden können .

Ein anderer Ansatz wäre, eine lokale Kopie der Liste zu erstellen und über die Kopie zu iterieren.

    
TomWolk 18.09.2013 14:12
quelle