Grundlegende Konzepte
1. Sichtbarkeit
Wenn ein Thread eine gemeinsam genutzte Variable ändert, kann ein anderer Thread den geänderten Wert lesen.
2. Speicherbarrieren
Eine Reihe von Anweisungen für den Prozessor, um sequentielle Einschränkungen für Speicheroperationen zu implementieren.
3. Pufferzeile
Die CPU teilt die kleinste Speichereinheit mit, die im Cache zugewiesen werden kann. Wenn der Prozessor die Cachezeile füllt, wird die gesamte Cachezeile geladen.
4. Präfixanweisungen sperren
Präfixanweisungen sperren bewirken auf Mehrkernprozessoren zwei Dinge:
1) Ändern Sie die Daten der Cache-Zeile der aktuellen Prozessorzuordnung zum Systemspeicher.
2) Durch diesen Vorgang des Zurückschreibens in den Speicher werden die von anderen CPUs an der Speicheradresse zwischengespeicherten Daten ungültig.
5. Cache-Kohärenzprotokoll
Bei Multiprozessoren garantiert Null, dass der Cache jedes Prozessors konsistent ist und jeder Prozessor die auf dem Bus weitergegebenen Daten abhört, um zu überprüfen, ob sie zwischengespeichert sind Wert ist abgelaufen. Wenn der Prozessor feststellt, dass die seiner Cache-Zeile entsprechende Adresse geändert wurde, setzt er die Cache-Zeile des aktuellen Prozessors in einen ungültigen Zustand. Wenn der Prozessor diese Daten liest oder schreibt, liest er die Daten erneut aus dem Speicher in den Prozessor-Cache.
6.CAS
CompareAndSwap Vergleichen und tauschen
Die CAS-Operation erfordert die Eingabe von zwei Werten, einem alten Wert (der Wert vor der Durchführung der CAS-Operation, der erwartete Wert) und einem Neuer Wert: Der aktuelle Wert kann nur dann auf den neuen Wert gesetzt werden, wenn der aktuelle Wert gleich dem alten Wert ist, andernfalls wird er nicht gesetzt. Dies ist eine atomare Operation, die durch Hardware garantiert wird.
7. Neuordnungsregeln
Grundsätzlich gibt es bei JMM nur eine Neuordnungsbeschränkung für Compiler und Prozessoren, solange sich die Ergebnisse der Programmausführung nicht ändern (bezogen auf Single-Threaded oder korrekt synchronisierte Multi). -Thread-Umgebung), dann können Compiler und Prozessor optimiert werden.
Volatile
Wie aus der obigen Lock-Prefix-Anweisung und dem Cache-Konsistenzprotokoll ersichtlich ist, ist dies das Implementierungsprinzip von Volatile.
Tatsächlich wird beim Schreiben der valatilen Variablen tatsächlich ein Lock-Präfix hinzugefügt, um den Zweck der Sichtbarkeit zu erreichen.
final
Das Final-Feld kann nur einmal explizit zugewiesen werden. Dies bedeutet jedoch nicht, dass das Final-Feld nicht mehrmals initialisiert werden kann.
Zum Beispiel: final int i; bevor i im Konstruktor zugewiesen wird, wird es auf den Standardwert 0 initialisiert. Dies kann durch Debuggen des Codes nachgewiesen werden.
Um sicherzustellen, dass vor der Initialisierung nicht auf den Wert des letzten Felds zugegriffen wird, muss der Programmierer nur eines sicherstellen: Das heißt, im Konstruktor hat das zu erstellende Objekt (dies) nicht „Escape“, dann kann ohne Synchronisationsmittel sichergestellt werden, dass die endgültigen Felder, die von jedem Thread gesehen werden, einschließlich Basistypen und Referenztypen, durch den Konstruktor korrekt initialisiert wurden.
Ein Beispiel, bei dem ein zu konstruierendes Objekt entkommen würde:
public class FinalTest{ final int i; static FinalTest obj; public FinalTest(){ i =1; /** *这里会使正在被构造的对象逸出,如果和上一句做了重排序,那么其他线程就可以通过obj访问到还为被初始化的final域。 **/ obj = this; } }
Happens-Before-Regel
Happens-before Die Bedeutung von
Die Happen-Before-Regel wird verwendet, um die sequentielle Beziehung zwischen zwei Vorgängen zu beschreiben. Diese beiden Vorgänge können in einem Thread oder nicht im selben Thread erfolgen. Diese Reihenfolge bedeutet nicht unbedingt die Reihenfolge der Ausführungszeit, sondern vielmehr, dass die Ergebnisse der vorherigen Operation für die nachfolgende Operation sichtbar sind.
Die Happens-Before-Beziehung ist wie folgt definiert:
Wenn eine Operation vor einer anderen Operation geschieht, ist das Ausführungsergebnis der ersten Operation für die zweite Operation sichtbar , Und die Ausführungsreihenfolge der ersten Operation ist vor der zweiten Operation
Zwischen den beiden Operationen besteht eine Vorher-Beziehung, was nicht bedeutet, dass es sich um eine spezifische Implementierung der Java-Plattform handelt muss den Ereignissen folgen. Die durch die -before-Beziehung angegebene Reihenfolge wird ausgeführt. Wenn das Ausführungsergebnis nach der Neuordnung mit dem Ausführungsergebnis gemäß der Havarie-Before-Beziehung übereinstimmt, ist diese Neuordnung nicht illegal.
Wenn beispielsweise A in der Programmausführungsreihenfolge vor B steht und A eine gemeinsam genutzte Variable ändert und B zufällig die gemeinsam genutzte Variable verwendet, dann muss A vor B geschehen Um es einfacher zu machen: Die Änderung der gemeinsam genutzten Variablen durch A muss für B sichtbar sein, wenn B sie ausführt.
passiert-bevor-Regel
Programmsequenzregel: Jede Operation in einem Thread wird ausgeführt, bevor eine nachfolgende Operation in diesem Thread ausgeführt wird.
Schlossregeln überwachen: Das Entsperren eines Schlosses erfolgt – bevor das Schloss anschließend gesperrt wird.
flüchtige Regel: Ein Schreibvorgang in ein flüchtiges Feld erfolgt – bevor ein nachfolgender Lesevorgang dieses flüchtigen Felds erfolgt.
Transitivität: Wenn A vor B passiert und B vor C passiert, dann passiert A vor C.
start()-Regel: Wenn Thread A die Operation ThreadB.start() ausführt, dann erfolgt die ThreadB.start()-Operation von Thread A – vor jeder Operation in Thread B.
join()-Regel: Wenn Thread A die Operation ThreadB.join() ausführt und erfolgreich zurückkehrt, dann erfolgt jede Operation von Thread B – vor der Operation von Thread A von ThreadB.join () Erfolgreich zurückkehren.
Erklärung all dieser Regeln: „Apassiert vor B“ bedeutet nicht, dass A vor B passieren muss, sondern dass, wenn A vor B passiert ist, die Operationsergebnisse von A sichtbar sein müssen nach B