web-dev-qa-db-de.com

Werden statische Variablen zwischen Threads geteilt?

Mein Lehrer in einer höheren Klasse Java zum Thema Threading sagte etwas, dessen ich mir nicht sicher war.

Er erklärte, dass der folgende Code die Variable ready nicht unbedingt aktualisieren würde. Ihm zufolge teilen sich die beiden Threads nicht unbedingt die statische Variable, insbesondere wenn jeder Thread (Hauptthread versus ReaderThread) auf einem eigenen Prozessor läuft und daher nicht dieselben Register/Cache/etc und eine CPU aktualisiert die andere nicht.

Im Wesentlichen sagte er, dass es möglich ist, dass ready im Hauptthread, aber NICHT im ReaderThread aktualisiert wird, so dass ReaderThread eine Endlosschleife ausführt.

Er behauptete auch, dass das Programm 0 Oder 42 Drucken könne. Ich verstehe, wie 42 Gedruckt werden kann, aber nicht 0. Er erwähnte, dass dies der Fall wäre, wenn die Variable number auf den Standardwert gesetzt würde.

Ich dachte, es ist vielleicht nicht garantiert, dass die statische Variable zwischen den Threads aktualisiert wird, aber das erscheint mir für Java sehr merkwürdig. Behebt die Flüchtigkeit von ready dieses Problem?

Er zeigte diesen Code:

public class NoVisibility {  
    private static boolean ready;  
    private static int number;  
    private static class ReaderThread extends Thread {   
        public void run() {  
            while (!ready)   Thread.yield();  
            System.out.println(number);  
        }  
    }  
    public static void main(String[] args) {  
        new ReaderThread().start();  
        number = 42;  
        ready = true;  
    }  
}
91
dontocsata

Statische Variablen haben in Bezug auf die Sichtbarkeit nichts Besonderes. Wenn auf sie zugegriffen werden kann, kann jeder Thread auf sie zugreifen, sodass Sie mit größerer Wahrscheinlichkeit Parallelitätsprobleme feststellen, da sie exponierter sind.

Es gibt ein Sichtbarkeitsproblem, das durch das Speichermodell der JVM verursacht wird. In diesem Artikel geht es um das Speichermodell und wie Schreibvorgänge für Threads sichtbar werden. . Sie können nicht damit rechnen, dass Änderungen, die ein Thread für andere Threads sichtbar macht, rechtzeitig erfolgen (tatsächlich ist die JVM nicht verpflichtet, diese Änderungen für Sie zu irgendeinem Zeitpunkt überhaupt sichtbar zu machen), es sei denn, Sie richten ein passiert-vor-Beziehung .

Hier ist ein Zitat aus diesem Link (im Kommentar von Jed Wesley-Smith angegeben):

Kapitel 17 der Java Sprachspezifikation definiert die Vorgangsrelation für Speicheroperationen wie Lesen und Schreiben von gemeinsam genutzten Variablen. Die Ergebnisse eines Schreibvorgangs durch einen Thread sind für einen Lesevorgang garantiert sichtbar von einem anderen Thread nur dann, wenn die Schreiboperation vor der Leseoperation ausgeführt wird Die synchronisierten und flüchtigen Konstrukte sowie die Methoden Thread.start () und Thread.join () können Vor-Zwischen-Beziehungen erstellen.

  • Jede Aktion in einem Thread findet statt - vor jeder Aktion in diesem Thread, die später in der Programmreihenfolge erfolgt.

  • Eine Freigabe (synchronisierter Block oder Methoden-Exit) eines Monitors erfolgt vor jeder nachfolgenden Sperre (synchronisierter Block oder Methoden-Eintrag) desselben Monitors. Und da die Beziehung "happen-before" transitiv ist, werden alle Aktionen eines Threads vor dem Entsperren ausgeführt, bevor alle Aktionen nach dem Sperren eines Threads von diesem Monitor ausgeführt werden.

  • Ein Schreibvorgang in ein flüchtiges Feld erfolgt vor jedem nachfolgenden Lesevorgang desselben Felds. Das Schreiben und Lesen von flüchtigen Feldern hat ähnliche Auswirkungen auf die Speicherkonsistenz wie das Betreten und Verlassen von Monitoren, beinhaltet jedoch keine gegenseitige Ausschlusssperre.

  • Ein Aufruf zum Starten eines Threads erfolgt, bevor eine Aktion im gestarteten Thread ausgeführt wird.

  • Alle Aktionen in einem Thread werden ausgeführt, bevor ein anderer Thread erfolgreich von einem Join in diesem Thread zurückgegeben wird.

74
Nathan Hughes

Er sprach von Sichtbarkeit und war nicht zu wörtlich zu nehmen.

Statische Variablen werden zwar von mehreren Threads gemeinsam genutzt, die in einem Thread vorgenommenen Änderungen sind jedoch möglicherweise nicht sofort für einen anderen Thread sichtbar, sodass es den Anschein hat, als gäbe es zwei Kopien der Variablen.

Dieser Artikel enthält eine Ansicht, die mit der Darstellung der Informationen übereinstimmt:

Zuerst müssen Sie etwas über das Java) - Speichermodell verstehen. Ich habe mich über die Jahre ein bisschen bemüht, es kurz und gut zu erklären. Ab heute die beste Art, wie ich denken kann von zu beschreiben ist, wenn man es sich so vorstellt:

  • Jeder Thread in Java findet in einem separaten Speicherbereich statt (dies ist eindeutig nicht wahr, also nehmen Sie diesen mit).

  • Sie müssen spezielle Mechanismen verwenden, um zu gewährleisten, dass die Kommunikation zwischen diesen Threads wie bei einem Nachrichtenübermittlungssystem erfolgt.

  • Speicherschreibvorgänge, die in einem Thread stattfinden, können "durchlaufen" und von einem anderen Thread gesehen werden, dies ist jedoch keinesfalls garantiert. Ohne explizite Kommunikation können Sie nicht garantieren, welche Schreibvorgänge von anderen Threads gesehen werden oder in welcher Reihenfolge sie gesehen werden.

...

thread model

Aber auch dies ist nur ein mentales Modell, um über Threading und Volatilität nachzudenken, und nicht wörtlich, wie die JVM funktioniert.

35
Bert F

Grundsätzlich ist es wahr, aber tatsächlich ist das Problem komplexer. Die Sichtbarkeit gemeinsam genutzter Daten kann nicht nur durch CPU-Caches, sondern auch durch die Ausführung von Befehlen außerhalb der Reihenfolge beeinträchtigt werden.

Daher definiert Java ein Speichermodell , das angibt, unter welchen Umständen Threads den konsistenten Status der gemeinsam genutzten Daten sehen können.

In Ihrem speziellen Fall garantiert das Hinzufügen von volatile die Sichtbarkeit.

12
axtavt

Sie werden natürlich in dem Sinne "geteilt", dass sie sich beide auf dieselbe Variable beziehen, aber sie sehen nicht unbedingt die Aktualisierungen des jeweils anderen. Dies gilt für jede Variable, nicht nur für statische.

Und theoretisch können von einem anderen Thread erstellte Schreibvorgänge in einer anderen Reihenfolge angezeigt werden, es sei denn, die Variablen werden als volatile deklariert oder die Schreibvorgänge werden explizit synchronisiert.

8
biziclop

Innerhalb eines einzelnen Klassenladeprogramms werden statische Felder immer gemeinsam genutzt. Um Daten explizit auf Threads zu verteilen, sollten Sie eine Funktion wie ThreadLocal verwenden.

4
Kirk Woll

Wenn Sie statische primitive Typvariablen initialisieren, weist Java default einen Wert für statische Variablen zu

public static int i ;

wenn Sie die Variable wie folgt definieren, ist der Standardwert von i = 0. Aus diesem Grund besteht die Möglichkeit, dass Sie 0 erhalten. Dann aktualisiert der Hauptthread den Wert von boolean ready auf true. Da ready eine statische Variable ist, verweisen der Haupt-Thread und der andere Thread auf dieselbe Speicheradresse, sodass sich die ready-Variable ändert. so dass der sekundäre Thread aus while-Schleife und Druckwert rauskommt. Beim Drucken des Werts ist der initialisierte Wert von number 0. Wenn der Thread-Prozess die Variable while loop before main thread update number durchlaufen hat. dann gibt es die möglichkeit 0 zu drucken

2
NuOne