2011-01-03 16 views
4

Stellen Sie sich einen Collection synchronisiert:Wie klonst du eine synchronisierte Sammlung?

Set s = Collections.synchronizedSet(new HashSet()) 

Was ist der beste Ansatz ist diese Sammlung zu klonen?

Es wird bevorzugt, dass das Klonen keine Synchronisierung in der ursprünglichen Sammlung erfordert, sondern dass das Iterieren über die geklonte Sammlung keine Synchronisierung in der ursprünglichen Sammlung erfordert.

+0

Was sind die Bedingungen der Kopie? Kann sich das Original während des Kopierens ändern? Was versuchst du mit dem Klon zu erreichen? (kann Ihnen vielleicht einen effizienteren Weg vorschlagen, Ihre Ziele zu erreichen, aber Sie brauchen die Antwort auf diese Fragen, um sicherzustellen, dass es für Sie richtig ist) –

+0

@Berin Loritsch: Ja, das Original-Set kann sich während des Kopierens ändern. Ich versuche, einen Iterator von einem Set zu bekommen, das im Sinne von Daten und Synchronisation von der ursprünglichen Collection unabhängig sein sollte. – MRalwasser

Antwort

6

Verwenden Sie eine Kopie-Konstruktor in einem synchronisierten Block:

synchronized (s) { 
    Set newSet = new HashSet(s); //preferably use generics 
} 

Wenn Sie die Kopie müssen auch synchronisiert werden, dann Collections.synchronizedSet(..) wieder verwenden.

Laut Peters Kommentar - Sie müssen dies in einem synchronisierten Block auf dem ursprünglichen Satz tun. Die Dokumentation von synchronizedSet ist ausdrücklich dazu:

Es ist zwingend notwendig, dass der Benutzer manuell auf den zurückgegebenen Satz synchronisieren, wenn über sie iterieren

+3

Leider verwendet dies den Iterator unter der Motorhaube, so dass Sie (s) sicher synchronisieren müssen. –

+0

@Peter Ja, offensichtlich gibt es kaum eine Möglichkeit, auf eine Sammlung zuzugreifen, ohne Iteratoren zu verwenden –

+1

Die schlechte Seite der Geschichte ist, dass Sie wissen müssen, wie die Methode implementiert wird. – Bozho

1

Sie können den Satz nicht synchronisiert werden durch folgende Maßnahmen, die Belichtung vermeidet ein Iterator auf dem Original-Set.

Set newSet = new HashSet(Arrays.asList(s.toArray())); 

EDIT Von Collections.SynchronizedCollection

public Object[] toArray() { 
    synchronized(mutex) {return c.toArray();} 
} 

Wie Sie sehen können, wird die Sperre für die gesamte Zeit hielt die Operation ausgeführt wird. So wird eine sichere Kopie der Daten erstellt. Es spielt keine Rolle, ob ein Iterator intern verwendet wird. Das zurückgegebene Array kann threadsicher verwendet werden, da nur der lokale Thread einen Verweis darauf hat.

HINWEIS: Wenn Sie diese Probleme vermeiden möchten, empfehle ich Ihnen, ein Set aus der Parallelbibliothek in Java 5.0 in 2004 hinzugefügt. Ich schlage auch vor, dass Sie Generika verwenden, da dies Ihre Sammlungen mehr typsicher machen kann.

+0

verwendet es immer noch einen Iterator. – Bozho

+0

Wahr, aber es iteriert über eine Kopie. das ist threadsicher. toArray() ist threadsicher, da es atomar ist. –

+0

Ich sehe nirgendwo, dass "toArray()" atomar ist - zumindest in dem Sinne, der in diesem Fall von Bedeutung ist. Die einzige Garantie, die ich sehe, ist, dass der Aufruf von toArray() 'eine neue Kopie eines Arrays erstellt - auch wenn die Sammlung von einem Array unterstützt wird. Ich würde meinen Code nicht auf diese Annahme setzen wollen. Das Kopieren von Arrays benötigt Zeit, insbesondere wenn die Quelle von Knoten unterstützt wird (wie bei allen Sätzen und LinkedLists). In diesem Fall wird ein Iterator benötigt. –

3

Wenn Sie synchronisierte Mengen verwenden, müssen Sie verstehen, dass der Synchronisationsaufwand für den Zugriff auf jedes Element in der Menge anfällt. Die Collections.synchronizedSet() umschließt nur Ihr Set mit einer Shell, die jede Methode zur Synchronisierung zwingt. Wahrscheinlich nicht, was du wirklich wolltest. A ConcurrentSkipListSet bietet Ihnen eine bessere Leistung in einer Multithread-Umgebung, in der mehrere Threads in das Set schreiben.

Die ConcurrentSkipListSet ermöglicht es Ihnen, die folgenden auszuführen:

Set newSet = s.clone();//preferably use generics 

Es ist nicht ungewöhnlich zu verwenden, um einen Klon eines Satzes für Snapshot-Verarbeitung. Wenn Sie danach suchen, können Sie einen kleinen Code hinzufügen, um den Fall zu behandeln, in dem das Element bereits verarbeitet wurde. Der Overhead, der mit dem gelegentlichen Objekt einhergeht, das in mehr als einem Kopiersatz enthalten ist, ist normalerweise geringer als der konsistente Aufwand, Collections.concurrentSet() zu verwenden.

EDIT: Ich habe gerade festgestellt, dass ConcurrentSkipListSet Cloneable ist und bietet eine threadsafe clone() Methode. Ich habe meine Antwort geändert, weil ich wirklich glaube, dass dies die beste Option ist - anstatt die Skalierbarkeit und Leistung auf Collections.concurrentSet() zu verlieren.