Heim  >  Artikel  >  Java  >  [Bekämpfung der Java-Parallelität] ----- Neuordnung des Java-Speichermodells

[Bekämpfung der Java-Parallelität] ----- Neuordnung des Java-Speichermodells

黄舟
黄舟Original
2017-02-24 10:05:261214Durchsuche

Um die Leistung zu gewährleisten, ordnen Prozessoren und Compiler die Anweisungen häufig neu an. Sie können sie jedoch nicht beliebig sortieren. Es müssen die folgenden zwei Bedingungen erfüllt sein >

1. Die Ergebnisse der Programmausführung können in einer Single-Threaded-Umgebung nicht geändert werden.

2. Eine Neuordnung ist nicht zulässig, wenn Datenabhängigkeiten bestehen.

Wenn Sie die vorherigen Informationen von LZ gelesen haben Blog, Sie werden wissen, dass diese beiden Punkte tatsächlich auf eines zurückzuführen sind: Sie können nicht durch das Vorher-Geschehen-Prinzip abgeleitet werden, und JMM ermöglicht eine beliebige Reihenfolge.

Als-ob-serielle Semantik

Als-ob-serielle Semantik bedeutet, dass alle Vorgänge zur Optimierung neu angeordnet werden können, Sie müssen jedoch sicherstellen, dass sie nach der Neuordnung ausgeführt werden. Das Ergebnis kann nicht geändert werden. und der Compiler, die Laufzeit und der Prozessor müssen sich an die Semantik „Als ob seriell“ halten. Beachten Sie, dass „as-if-serial“ nur eine Single-Thread-Umgebung garantiert und in einer Multi-Thread-Umgebung ungültig ist.

Lassen Sie uns ein einfaches Beispiel veranschaulichen:

int a = 1 ;      //A
int b = 2 ;      //B
int c = a + b;   //C
Die drei Operationen von A, B und C haben die folgende Beziehung: A und B haben keine Datenabhängigkeiten , Es besteht eine Datenabhängigkeitsbeziehung zwischen B und C, daher können A und B bei der Neuordnung beliebig sortiert werden, sie müssen jedoch vor C stehen. Die Ausführungsreihenfolge kann A –> B –> > A – >C. Unabhängig von der Ausführungsreihenfolge ist das Endergebnis C jedoch immer gleich 3.

As-if-Serail-Semantik schützt Single-Thread-Programme, wodurch sichergestellt werden kann, dass das Endergebnis des Programms unter der Voraussetzung der Neuordnung immer konsistent ist.

Tatsächlich haben sie für den obigen Code eine solche „Geschehen-vorher“-Beziehung:

  1. A geschieht vor B

  2. B passiert vor C

  3. A passiert vor C

1 und 2 sind Programmsequenzregeln und 3 ist Transitivität. Bedeutet das jedoch nicht, dass B durch Neuordnung vor A ausgeführt werden kann? Warum geschieht A vor B? Auch hier heißt es, dass A vor B geschieht, was nicht bedeutet, dass A definitiv vor B ausgeführt wird, sondern dass A für B sichtbar ist, aber relativ zu diesem Programm müssen die Ausführungsergebnisse von A für B nicht sichtbar sein. und ihre Neuordnung hat keinen Einfluss auf die Ergebnisse. Daher wird JMM diese Neuordnung nicht als illegal betrachten.

Wir müssen dies verstehen: Verbessern Sie die Betriebseffizienz des Programms so weit wie möglich, ohne die Ausführungsergebnisse des Programms zu ändern.

Unten sehen wir uns einen interessanten Codeabschnitt an:

public class RecordExample1 {
    public static void main(String[] args){        
    int a = 1;        
    int b = 2;        
    try {
            a = 3;           //A
            b = 1 / 0;       //B
        } catch (Exception e) {

        } finally {
            System.out.println("a = " + a);
        }
    }
}
Gemäß den Neuordnungsregeln können Operation A und Operation B neu angeordnet werden. Wenn sie neu angeordnet werden, löst B An aus Ausnahme auftritt (/ durch Null). Zu diesem Zeitpunkt wird Anweisung A definitiv nicht ausgeführt. Wird a immer noch gleich 3 sein? Wenn Sie dem Als-ob-seriell-Prinzip folgen, ändert sich das Ergebnis des Programms. Tatsächlich führt die JVM eine spezielle Verarbeitung für Ausnahmen durch, um die Semantik als ob seriell sicherzustellen = 3), obwohl dadurch die Logik in cathc komplizierter wird, lautet das Prinzip der JIT-Optimierung: Optimieren Sie die Logik im normalen Betrieb des Programms so weit wie möglich, auch auf Kosten der Komplexität der Catch-Block-Logik.

Die Auswirkungen der Neuordnung auf Multithreading

In einer Single-Thread-Umgebung kann die Neuordnung aufgrund der Semantik, als ob sie seriell ist, keinen Einfluss auf das Endergebnis haben, aber was ist mit einer Multi-Thread-Umgebung? ?

Der folgende Code (klassische Verwendung von volatile):

public class RecordExample2 {
    int a = 0;    boolean flag = false;    
    /**
     * A线程执行
     */
    public void writer(){
        a = 1;                  
        // 1
        flag = true;            
        // 2
    }    /**
     * B线程执行
     */
    public void read(){        
    if(flag){                  
    // 3
           int i = a + a;          
           // 4
        }
    }

}
Thread A führt write() aus, Thread B führt read() aus. Kann Thread B während der Ausführung a = 1 lesen? Tuch? Die Antwort ist nicht unbedingt (

Hinweis: X86CPU unterstützt keine Schreib-Schreib-Neuordnung. Wenn es auf x86 betrieben wird, ist dies definitiv a=1. LZ hat es lange Zeit nicht getestet und es schließlich erst später herausgefunden Überprüfen der Informationen ).

Da zwischen Operation 1 und Operation 2 keine Datenabhängigkeit besteht, kann eine Neuordnung durchgeführt werden. Es besteht keine Datenabhängigkeit zwischen Operation 3 und Operation 4. Sie können auch neu angeordnet werden, aber die Operationen 3 und 4 Es gibt eine Kontrollabhängigkeit zwischen Operationen 4. Wenn Operation 1 und Operation 2 neu angeordnet werden:

[Bekämpfung der Java-Parallelität] ----- Neuordnung des Java-Speichermodells

Gemäß dieser Ausführungsreihenfolge kann Thread B den von Thread A festgelegten a-Wert definitiv nicht lesen. Die Semantik von Multithreading hier ist Es wurde durch Neuordnung zerstört.

Operation 3 und Operation 4 können auch nachbestellt werden, was hier nicht erläutert wird. Es besteht jedoch eine Kontrollabhängigkeitsbeziehung zwischen ihnen, da Operation 4 nur ausgeführt wird, wenn Operation 3 eingerichtet ist. Wenn im Code Steuerungsabhängigkeiten vorhanden sind, wirkt sich dies auf die Parallelität der Ausführung der Befehlssequenz aus. Daher verwenden Compiler und Prozessoren die Schätzung der Ausführung, um die Auswirkungen von Steuerungsabhängigkeiten auf die Parallelität zu überwinden. Wenn Operation 3 und Operation 4 neu angeordnet werden und Operation 4 zuerst ausgeführt wird, wird das Berechnungsergebnis vorübergehend im Neuordnungspuffer gespeichert. Wenn Operation 3 wahr ist, wird das Berechnungsergebnis in die Variable i geschrieben

Durch Die obige Analyse hat

eine Neuordnung, die sich nicht auf die Ausführungsergebnisse einer Single-Thread-Umgebung auswirkt, sondern die Ausführungssemantik von Multi-Threads zerstört .

Das Obige ist der Inhalt von [Java Concurrency] ----- Neuordnung des Java-Speichermodells Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website (www.php.cn)!



Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn