Mein Lehrer in einer Java-Klasse der Oberstufe zum Thema Threading sagte etwas, dessen ich mir nicht sicher war.
Er erklärte, dass der folgende Code die ready
Variable nicht unbedingt aktualisieren würde . Ihm zufolge teilen sich die beiden Threads nicht unbedingt die statische Variable, insbesondere in dem Fall, dass jeder Thread (Hauptthread versus ReaderThread
) auf einem eigenen Prozessor ausgeführt wird und daher nicht dieselben Register / Cache / etc und eine CPU gemeinsam nutzt wird den anderen nicht aktualisieren.
Im Wesentlichen sagte er, es sei möglich, dass ready
im Haupt-Thread aktualisiert wird, aber NICHT im ReaderThread
, so dass sich ReaderThread
eine Endlosschleife ergibt.
Er behauptete auch, es sei möglich, dass das Programm drucke 0
oder 42
. 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 auf den Standardwert gesetzt wird.
Ich dachte, es ist vielleicht nicht garantiert, dass die statische Variable zwischen den Threads aktualisiert wird, aber das kommt mir für Java sehr seltsam vor. Behebt das ready
Volatilisieren 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;
}
}
quelle
Antworten:
Statische Variablen haben nichts Besonderes, wenn es um Sichtbarkeit geht. Wenn auf sie zugegriffen werden kann, kann jeder Thread auf sie zugreifen, sodass es wahrscheinlicher ist, dass Parallelitätsprobleme auftreten, da sie stärker gefährdet sind.
Das Speichermodell der JVM weist ein Sichtbarkeitsproblem auf. In diesem Artikel geht es um das Speichermodell und darum, wie Schreibvorgänge für Threads sichtbar werden . Sie können sich nicht darauf verlassen, dass Änderungen, die ein Thread macht, für andere Threads rechtzeitig sichtbar werden (tatsächlich ist die JVM nicht verpflichtet, diese Änderungen in jedem Zeitraum für Sie sichtbar zu machen), es sei denn, Sie stellen eine Beziehung her, bevor dies geschieht .
Hier ist ein Zitat von diesem Link (im Kommentar von Jed Wesley-Smith):
quelle
Er sprach von Sichtbarkeit und nicht zu wörtlich zu nehmen.
Statische Variablen werden zwar von Threads gemeinsam genutzt, aber die in einem Thread vorgenommenen Änderungen sind 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:
Aber auch dies ist einfach ein mentales Modell, um über Threading und Volatilität nachzudenken, nicht wörtlich, wie die JVM funktioniert.
quelle
Grundsätzlich ist es wahr, aber tatsächlich ist das Problem komplexer. Die Sichtbarkeit gemeinsam genutzter Daten kann nicht nur durch CPU-Caches beeinträchtigt werden, sondern auch durch die fehlerhafte Ausführung von Anweisungen.
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
volatile
garantiert das Hinzufügen die Sichtbarkeit.quelle
Sie werden natürlich in dem Sinne "geteilt", dass sie beide auf dieselbe Variable verweisen, aber sie sehen nicht unbedingt die Aktualisierungen des anderen. Dies gilt für jede Variable, nicht nur für statische.
Und theoretisch können Schreibvorgänge, die von einem anderen Thread ausgeführt werden, in einer anderen Reihenfolge erscheinen, es sei denn, die Variablen werden deklariert
volatile
oder die Schreibvorgänge werden explizit synchronisiert.quelle
Innerhalb eines einzelnen Klassenladeprogramms werden statische Felder immer gemeinsam genutzt. Um Daten explizit auf Threads zu beschränken, möchten Sie eine Funktion wie verwenden
ThreadLocal
.quelle
Beim Initialisieren einer statischen primitiven Typvariablen weist Java Default einen Wert für statische Variablen zu
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 to true. Da ready eine statische Variable ist, verweisen der Hauptthread und der andere Thread auf dieselbe Speicheradresse, sodass sich die Ready-Variable ändert. Der sekundäre Thread verlässt also die while-Schleife und druckt den Wert. Beim Drucken ist der initialisierte Wert der Zahl 0. Wenn der Thread-Prozess während der Schleife vor der Variablen für die Aktualisierung der Haupt-Thread-Nummer durchlaufen wurde. dann besteht die Möglichkeit 0 zu drucken
quelle
@dontocsata du kannst zu deinem Lehrer zurückkehren 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 sich die folgenden Wörter in der angegebenen Reihenfolge auf diesen speziellen Fall beziehen.
Die folgenden 2 Variablen befinden sich in praktisch jeder bekannten Architektur in derselben Cache-Zeile.
Thread.exit
(Haupt-Thread) wird garantiert beendet undexit
verursacht aufgrund der Entfernung des Thread-Gruppen-Threads (und vieler anderer Probleme) garantiert einen Speicherzaun. (Es ist ein synchronisierter Aufruf, und ich sehe keine einzige Möglichkeit, ohne den Synchronisierungsteil implementiert zu werden, da die ThreadGroup ebenfalls beendet werden muss, wenn keine Daemon-Threads mehr vorhanden sind usw.).Der gestartete Thread
ReaderThread
wird den Prozess am Leben erhalten, da es sich nicht um einen Daemon handelt! Somit wirdready
undnumber
zusammen gespült (oder die Nummer vorher, wenn ein Kontextwechsel auftritt) und es gibt in diesem Fall keinen wirklichen Grund für eine Neuordnung, zumindest kann ich mir nicht einmal einen vorstellen. Sie werden etwas wirklich Seltsames brauchen, um etwas anderes zu sehen42
. Ich gehe wieder davon aus, dass sich beide statischen Variablen in derselben Cache-Zeile befinden. Ich kann mir einfach keine 4 Byte lange Cache-Zeile oder eine JVM vorstellen, die sie nicht in einem durchgehenden Bereich (Cache-Zeile) zuweist.quelle