2012-08-22 4 views
5

Ich habe eine synchronisierte Karte (via Collections.synchronizedMap()), die durch Gewinde A. Thema B gelesen und aktualisiert greift auf die Karte nur über Map.keySet() (read-only).Wie wird die Map zwischen einem r/w-Thread und einem schreibgeschützten Thread synchronisiert?

Wie soll ich diese synchronisieren? Die docs say dass keySet() (für eine Collections.synchronizedMap) "nicht in synchronisierten Block sein muss". Ich kann den Lese-/Schreibzugriff von Thread A innerhalb eines synchronisierten Blocks setzen, aber ist das überhaupt notwendig?

Ich denke, es seltsam, mir scheint sogar eine synchronisierte Karte zu verwenden, oder einen synchronisierten Block, wenn Map.keySet muss nicht synchronisiert werden (nach dem docs Link oben) ...

Update: Ich habe verpasst, dass die Iteration des Schlüsselsatzes synchronisiert werden muss, obwohl das Abrufen des Schlüsselsatzes keine Synchronisierung erfordert. Nicht besonders aufregend, den keySet zu haben, ohne durchschauen zu können, also Endergebnis = Synchronisation erforderlich. Verwenden Sie stattdessen eine ConcurrentHashMap.

+0

ist das über Java3D? –

+0

@ tuğrulbüyükışık nein. Allgemeine Java-Parallelitätsfrage – ericsoco

Antwort

2

Um eine wirklich Lese-/Schreib zu machen im Vergleich Lese-/nur Map Wrapper sperren, können Sie einen Blick auf die Wrapper nehmen die Collections für synchronizedMap() verwendet und mit einem ReentrantReadWriteLock alle der synchronized Aussagen ersetzen. Das ist ein gutes Stück Arbeit. Stattdessen sollten Sie in Erwägung ziehen, eine ConcurrentHashMap zu verwenden, die dort alle richtigen Dinge tut.

In Bezug auf die keySet(), es muss nicht in einem synchronized Block sein, weil es bereits synchronized durch die Collections.synchronizedMap() sein wird. Die Javadocs weist nur darauf hin, dass Sie beim Synchronisieren der Map darauf synchronisieren müssen, da Sie mehrere Operationen ausführen, aber Sie müssen nicht synchronisieren, wenn Sie die keySet() erhalten, die in eine SynchronizedSet Klasse eingepackt ist macht seine eigene Synchronisation.

Schließlich schien Ihre Frage zu implizieren, dass Sie nicht auf etwas synchronisieren müssen, wenn Sie gerade davon lesen. Sie müssen sich daran erinnern, dass die Synchronisierung nicht nur vor rauen Bedingungen schützt, sondern auch sicherstellt, dass die Daten von jedem der Prozessoren ordnungsgemäß gemeinsam genutzt werden. Selbst wenn Sie auf eine Map als schreibgeschützt zugreifen, müssen Sie immer noch darauf synchronisieren, wenn ein anderer Thread sie aktualisiert.

+1

Ich denke, ich weiß, was Sie sagen wollen, aber 'keySet()' * ist nicht * eine "Kopie der Schlüssel in der' Map' "(es ist eine Live-Ansicht), und es darf nicht sein muss im synchronisierten Block sein. Wenn es sich wirklich um eine Kopie handelt und Sie es nicht innerhalb des synchronisierten Blocks erhalten haben, könnte Ihre Iteration über veraltete Daten sein. Um sicherzustellen, dass es frisch ist, müssen Sie die Methode 'keySet()' in die Synchronisation einbeziehen. –

+0

+1 für ConcurrentHashMap Vorschlag, ich werde es untersuchen. – ericsoco

+0

Ich wusste, dass es normalerweise @Mark war, aber ich habe mir den Code nicht angesehen. Jetzt sehe ich, dass es in ein 'SynchronizedSet' verpackt wird. Ich werde meine Antwort ändern. Vielen Dank. – Gray

2

Die docs sagen Ihnen, wie man richtig mehrstufiges Operationen synchronisieren, die atomar sein müssen, in diesem Fall über die Karte Iterieren:

Map m = Collections.synchronizedMap(new HashMap()); 
     ... 
Set s = m.keySet(); // Needn't be in synchronized block 
     ... 
synchronized(m) { // Synchronizing on m, not s! 
    Iterator i = s.iterator(); // Must be in synchronized block 
    while (i.hasNext()) 
     foo(i.next()); 
} 

Hinweis, wie die tatsächliche Iteration in synchronisierter sein muß Block. Die Dokumentation sagt nur, dass es keine Rolle spielt, ob die keySet() im synchronisierten Block ist, weil es eine Live-Ansicht der Map ist. Wenn sich die Schlüssel in der Karte zwischen dem Bezug auf den Schlüsselsatz und dem Anfang des synchronisierten Blocks ändern, spiegelt der Schlüsselsatz diese Änderungen wider.

Und übrigens, die Dokumente, die Sie angeben, sind nur für eine Map von Collections.synchronizedMap zurückgegeben. Die Aussage tut nicht unbedingt gelten für alle Map s.

+0

Sehr guter Punkt, ich habe verpasst, dass die Iteration des Schlüsselsatzes synchronisiert werden muss. – ericsoco

2

Die Dokumente sind korrekt. Die Karte, die von Collections.synchronizedMap() zurückgegeben wird, wird ordnungsgemäß um alle Anrufe synchronisiert, die an das ursprüngliche Map gesandt werden.Das von keySet() zurückgegebene set impl weist jedoch nicht die gleiche Eigenschaft auf, daher müssen Sie sicherstellen, dass es unter derselben Sperre gelesen wird.

Ohne diese Synchronisation gibt es keine Garantie, dass Thread B jemals eine Aktualisierung von Themen A. gemacht sehen

Sie möchten vielleicht ConcurrentHashMap untersuchen. Es bietet nützliche Semantik für genau diesen Anwendungsfall. Das Iterieren über eine Sammlungsansicht in CHM (like keySet()) ergibt ein nützliches konkurrierendes Verhalten ("schwach konsistente" Iteratoren). Sie durchlaufen alle Schlüssel aus dem Status der Sammlung bei der Iteration, und Sie können Änderungen sehen oder nicht, nachdem der Iterator erstellt wurde.