2012-08-06 8 views
8

Verursacht der folgende Code die gleichen Probleme, wenn die Variable 'commonSet' dieser Methode stattdessen ein Feld auf Klassenebene ist. Wenn es sich um ein Feld auf Klassenebene handelt, muss das Hinzufügen zum Set-Vorgang innerhalb eines synchronisierten Blocks erfolgen, da HashSet nicht Thread-sicher ist. Sollte ich das gleiche im folgenden Code tun, da mehrere Threads zum Set hinzufügen oder sogar der aktuelle Thread fortfahren könnte, das Set zu mutieren.Thread Safe - letzte lokale Methodenvariable, die an Threads weitergegeben wurde?

public void threadCreatorFunction(final String[] args) { 
    final Set<String> commonSet = new HashSet<String>(); 

    final Runnable runnable = new Runnable() { 
     @Override 
     public void run() { 
      while (true) { 
       commonSet.add(newValue()); 
      } 
     } 
    }; 

    new Thread(runnable, "T_A").start(); 
    new Thread(runnable, "T_B").start(); 
} 

Der Verweis auf 'commonSet' 'verschlossen' durch endgültige Verwendung. Aber mehrere Threads, die darauf ausgeführt werden, können die Werte in der Gruppe immer noch beschädigen (sie können Duplikate enthalten?). Zweitens, Verwirrung ist seit "commonSet" ia eine Methode Level-Variable - es ist die gleiche Referenz wird auf dem Stapelspeicher der aufrufenden Methode (threadCreatorFunction) und Stapelspeicher der Lauf-Methoden - ist das korrekt?

Es gibt durchaus ein paar Fragen im Zusammenhang mit diesem:

Aber ich sie nicht auf Thread-sicher Teil eines solchen Austausch/Weitergabe von mutables betonte sehen kann.

+0

Siehe auch http://stackoverflow.com/questions/1299837/cannot-refer-to-a-non-final-variable-inside-an-inner-class-defined-in-a-differen – nos

Antwort

9

Nein, das ist absolut nicht threadsicher. Nur weil Sie es in einer endgültigen Variable haben, bedeutet dies, dass beide Threads die gleiche Referenz sehen, was in Ordnung ist - aber es macht das Objekt nicht mehr thread-sicher.

Entweder müssen Sie den Zugriff synchronisieren oder ConcurrentSkipListSet verwenden.

5

Ein interessantes Beispiel.

Die Referenz commonSet ist Thread sicher und unveränderlich. Es ist auf dem Stapel für den ersten Thread und ein Feld Ihrer anonymen Runnable Klasse. (Sie können dies in einem Debugger sehen)

Das Set commonSet bezieht sich auf mutable und nicht threadsicher. Sie müssen synchronisiert oder eine Sperre verwenden, um es threadsicher zu machen. (Oder einen Thread sichere Sammlung anstelle)

1

Ich glaube, Sie ein Wort in Ihrem ersten Satz fehlt:

Wird der folgende Code Ursache gleichen Probleme, wenn die Variable ‚commonSet‘ dieses Verfahrens war ein ??? stattdessen ein Feld auf Klassenebene.

Ich denke, Sie sind ein wenig verwirrt, obwohl. Die Gleichzeitigkeitsprobleme haben nichts damit zu tun, ob der Verweis auf Ihre veränderbare Datenstruktur als final deklariert ist oder nicht. Sie müssen die Referenz als final deklarieren, da Sie closing over innerhalb der anonymen inneren Klassendeklaration für Ihre Runnable sind. Wenn Sie tatsächlich mehrere Threads haben, die die Datenstruktur lesen/schreiben, müssen Sie entweder Sperren (Synchronisieren) verwenden oder eine gleichzeitige Datenstruktur wie java.util.concurrent.ConcurrentHashMap verwenden.

+0

korrigierte erste Zeile – haps10

+0

Ah, ich lag falsch - du hattest ein zusätzliches Wort! – DaoWen

0

Wie andere bereits kommentiert haben, verwechseln Sie einige Konzepte, wie Finale und synchronisiert.

Ich denke, wenn Sie erklären, was Sie mit Ihrem Code erreichen möchten, wäre es viel einfacher, Ihnen zu helfen. Ich habe den Eindruck, dass dieses Code-Snippet eher ein Beispiel für den eigentlichen Code ist.

Einige Fragen: Warum ist der Satz in der Funktion definiert? Soll es unter Threads geteilt werden? Etwas, das mir ein Rätsel ist, dass Sie Kiste zwei Threads mit der gleichen Instanz der runnable

new Thread(runnable, "T_A").start(); 
    new Thread(runnable, "T_B").start(); 
0

Ob commonset von einzelnen Thread verwendet wird oder mehrere es ist nur der Verweis, der einmal unveränderlich ist für die endgültige Objekte (dh zugewiesen Sie kann nicht erneut einen anderen Obj-Verweis zuweisen), Sie können jedoch den Inhalt, der von diesem Objekt referenziert wird, immer noch mit diesem Verweis ändern.

Wenn es nicht endgültig ein Thread es wieder initialisiert haben könnte und verändert die Referenz commonSet = new HashSet<String>(); commonSet.add(newValue()); wobei diese beiden Gewinde zwei verschiedene commonsets verwenden kann, die wahrscheinlich nicht das, was Sie

1

Die commonSet geteilt wollen unter zwei Threads. Sie haben es als endgültig deklariert und somit haben Sie die Referenz unveränderlich gemacht (Sie können sie nicht neu zuweisen), aber die tatsächlichen Daten innerhalb des Sets sind immer noch änderbar. Angenommen, ein Thread fügt einige Daten ein und ein anderer Thread liest einige Daten aus. Immer wenn der erste Thread Daten einfügt, möchten Sie diesen Satz höchstwahrscheinlich sperren, sodass kein anderer Thread lesen kann, bis diese Daten geschrieben sind. Passiert das mit einem HashSet? Nicht wirklich.