Frage Werden statische Variablen zwischen Threads geteilt?


Mein Lehrer in einer höheren Java-Klasse beim Threading sagte etwas, von dem ich nicht sicher war.

Er stellte fest, dass der folgende Code nicht unbedingt den ready Variable. Seinen Worten nach teilen sich die beiden Threads nicht notwendigerweise die statische Variable, speziell dann, wenn jeder Thread (Hauptthread gegenüber ReaderThread) auf einem eigenen Prozessor läuft und daher nicht dieselben Register / Cache / etc und eins teilt CPU wird das andere nicht aktualisieren.

Im Wesentlichen hat er gesagt, dass das möglich ist ready wird im Hauptthread aktualisiert, aber NICHT im ReaderThread, so dass ReaderThread unendlich loopt. Er behauptete auch, es sei möglich, dass das Programm '0' oder '42' drucke. Ich verstehe, wie "42" gedruckt werden könnte, aber nicht "0". Er erwähnte, dass dies der Fall sein würde, wenn die number Variable wird auf den Standardwert gesetzt.

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. Macht Making ready volatile korrigieren dieses Problem?

Er zeigte diesen Code:

öffentliche Klasse NoVisibility {
    private static boolean ready;
    private statische Int-Nummer;
    private statische Klasse ReaderThread erweitert Thread {
        public void run () {
            while (! ready) Thread.yield ();
            System.out.println (Nummer);
        }
    }
    public static void main (Zeichenfolge [] args) {
        neues ReaderThread (). start ();
        Anzahl = 42;
        bereit = wahr;
    }
}

75
2018-02-08 15:25


Ursprung


Antworten:


Es gibt kein Sichtbarkeitsproblem für statische Variablen. Es gibt ein Sichtbarkeitsproblem, das durch das Speichermodell der JVM verursacht wird. Hier ist ein Artikel, der über das Speichermodell spricht und wie Schreibvorgänge für Threads sichtbar werden. Sie können sich nicht auf Änderungen verlassen, die ein Thread für andere Threads rechtzeitig sichtbar macht (tatsächlich ist die JVM nicht verpflichtet, diese Änderungen für Sie sichtbar zu machen), es sei denn, Sie stellen ein passiert-vor der Beziehung, hier ist ein Zitat von diesem Link (in dem Kommentar von Jed Wesley-Smith):

Kapitel 17 der Java-Sprachspezifikation definiert die "happen-before" -Relation für Speicheroperationen wie das Lesen und Schreiben von gemeinsam genutzten Variablen. Die Ergebnisse eines Schreibens durch einen Thread sind garantiert nur dann für ein Lesen durch einen anderen Thread sichtbar, wenn die Schreiboperation vor der Leseoperation stattfindet. Die synchronisierten und flüchtigen Konstrukte sowie die Thread.start () - und Thread.join () -Methoden können "happen-before" -Beziehungen bilden. Bestimmtes:

  • Jede Aktion in einem Thread geschieht vor jeder Aktion in diesem Thread, die später in der Reihenfolge des Programms kommt.

  • Ein Entsperren (synchronisierter Block oder Methodenexit) eines Monitors erfolgt vor jeder nachfolgenden Sperre (synchronisierter Block oder Methodeneintrag) desselben Monitors. Und da die "happes-before" -Beziehung transitiv ist, werden alle Aktionen eines Threads vor dem Entsperren ausgeführt - vor allen Aktionen, die auf einen Thread folgen, der diesen Monitor sperrt.

  • Ein Schreiben in ein flüchtiges Feld geschieht - vor jedem weiteren Lesen desselben Feldes. Schreib- und Lesevorgänge flüchtiger Felder haben ähnliche Auswirkungen auf die Speicherkonsistenz wie das Ein- und Ausblenden von Monitoren, aber keine gegenseitige Ausschließungssperre.

  • Ein Aufruf zum Starten eines Threads erfolgt vor einer Aktion im gestarteten Thread.

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


57
2018-02-08 15:31



Er hat darüber gesprochen Sichtweite und nicht zu wörtlich genommen werden.

Statische Variablen werden tatsächlich zwischen Threads geteilt, aber die in einem Thread vorgenommenen Änderungen sind möglicherweise für einen anderen Thread nicht sofort sichtbar, so dass es aussieht, als gäbe es zwei Kopien der Variablen.

Dieser Artikel präsentiert eine Ansicht, die mit der Darstellung der Informationen übereinstimmt:

Zuerst müssen Sie etwas über das Java-Speichermodell verstehen. Ich habe ein bisschen über die Jahre gekämpft, um es kurz und gut zu erklären. Ab heute ist der beste Weg, um es zu beschreiben, wenn Sie es sich so vorstellen:

  • Jeder Thread in Java findet in einem separaten Speicherraum statt (das ist eindeutig unwahr, also bitte mit mir).

  • Sie müssen spezielle Mechanismen verwenden, um zu gewährleisten, dass die Kommunikation zwischen diesen Threads stattfindet, so wie Sie es bei einem Nachrichtenübergabesystem tun würden.

  • Speicherschreibvorgänge, die in einem Thread passieren, können "durchlecken" und von einem anderen Thread gesehen werden, aber dies ist keinesfalls garantiert. Ohne explizite Kommunikation können Sie nicht garantieren, welche Schreibvorgänge von anderen Threads gesehen werden oder sogar von der Reihenfolge, in der sie angezeigt werden.

...

thread model

Aber auch hier handelt es sich einfach um ein mentales Modell, um über Threading und Flüchtigkeit nachzudenken, nicht buchstäblich, wie die JVM funktioniert.


28
2018-02-08 15:35



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

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

In Ihrem speziellen Fall, Hinzufügen volatile garantiert Sichtbarkeit.


9
2018-02-08 15:33



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

Und theoretisch können Schreibvorgänge, die von einem anderen Thread erstellt wurden, in einer anderen Reihenfolge erscheinen, sofern die Variablen nicht deklariert sind volatile oder die Schreibvorgänge werden explizit synchronisiert.


5
2018-02-08 15:34



Innerhalb eines einzelnen Classloaders werden statische Felder immer gemeinsam genutzt. Um Daten explizit auf Threads zu legen, möchten Sie eine Funktion wie ThreadLocal.


4
2018-02-08 15:27



Wenn Sie die statische primitive Typvariable 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; Deshalb gibt es eine Möglichkeit, Sie 0 zu bekommen. dann aktualisiert der Hauptthread den Wert von boolean ready auf true. Da ready eine statische Variable ist, verweisen Hauptthread und der andere Thread auf die gleiche Speicheradresse, so dass sich die ready-Variable ändert. so der sekundäre Thread aus while-Schleife und Druckwert herauskommen. Beim Drucken ist der initialisierte Wertewert der Zahl 0. Wenn der Threadprozess die While-Schleife vor der Aktualisierung der Hauptthreadnummer passiert hat. dann gibt es eine Möglichkeit 0 zu drucken


1
2018-04-19 14:31



@dontocsata du kannst zurück zu deinem Lehrer gehen und ihn ein wenig schulen :)

wenige Notizen aus der realen Welt und unabhängig davon, was Sie sehen oder erzählt werden. Bitte beachten Sie, dass die folgenden Wörter in genau dieser Reihenfolge auf diesen speziellen Fall bezogen sind.

Die folgenden 2 Variablen befinden sich auf der gleichen Cache-Zeile unter praktisch jeder bekannten Architektur.

private static boolean ready;  
private static int number;  

Thread.exit (Hauptthread) wird garantiert beendet und exit verursacht aufgrund der Entfernung der Threadgruppen-Threads (und vieler anderer Probleme) garantiert einen Speicherzaun. (Es ist ein synchronisierter Aufruf, und ich sehe keinen einzigen Weg, der ohne den Sync-Teil implementiert wird, da die ThreadGroup auch enden muss, wenn keine Daemon-Threads übrig sind, usw.).

Der gestartete Thread ReaderThread wird den Prozess am Leben erhalten, da es kein Daemon ist! So ready und number wird zusammengespült (oder die Nummer davor, wenn ein Kontextwechsel stattfindet) und es gibt keinen wirklichen Grund für die Neuordnung in diesem Fall, zumindest kann ich nicht einmal an einen denken. Sie werden etwas wirklich Seltsames brauchen, um etwas anderes zu sehen 42. Wiederum nehme ich an, dass sich beide statischen Variablen in der gleichen Cache-Zeile befinden. Ich kann mir einfach nicht vorstellen, eine Cache-Zeile 4 Bytes lang oder eine JVM, die sie nicht in einem kontinuierlichen Bereich (Cache-Zeile) zuweisen.


-2
2018-02-11 00:06