Heim  >  Artikel  >  Java  >  Wissenspunkte zur gleichzeitigen Programmierung für das Java-Thread-Lernen

Wissenspunkte zur gleichzeitigen Programmierung für das Java-Thread-Lernen

WBOY
WBOYnach vorne
2022-06-22 12:14:281509Durchsuche

Dieser Artikel vermittelt Ihnen relevantes Wissen über Java, in dem hauptsächlich Probleme im Zusammenhang mit der gleichzeitigen Programmierung behandelt werden, einschließlich des Java-Speichermodells, einer detaillierten Erklärung von Volatile und des Implementierungsprinzips von Synchronized usw. Schauen wir uns das gemeinsam an es hilft allen.

Wissenspunkte zur gleichzeitigen Programmierung für das Java-Thread-Lernen

Empfohlenes Studium: „Java-Video-Tutorial

1. JMM-Grundlagen – Computerprinzipien

Das Java-Speichermodell ist das Java Memory Model, kurz JMM. JMM definiert, wie die Java Virtual Machine (JVM) im Computerspeicher (RAM) funktioniert. JVM ist das gesamte virtuelle Computermodell, daher ist JMM mit JVM verbunden. Die Java1.5-Version wurde überarbeitet und das aktuelle Java verwendet immer noch die Java1.5-Version. Die bei Jmm auftretenden Probleme ähneln denen moderner Computer.
Parallelitätsprobleme bei physischen Computern weisen viele Ähnlichkeiten mit den Situationen bei virtuellen Maschinen auf. Das Parallelitätsbehandlungsschema physischer Maschinen hat auch erhebliche Referenzbedeutung für die Implementierung virtueller Maschinen. D Laut dem „Report of Jeff Dean in the GOOGLE Plot of the Google“ können wir sehen, dass

Wissenspunkte zur gleichzeitigen Programmierung für das Java-Thread-Lernen

Computer sich in der Reaktionszeit unterscheiden, wenn sie einige unserer üblichen Grundoperationen ausführen.

Die folgenden Fälle dienen nur zur Veranschaulichung und stellen nicht die tatsächliche Situation dar.

Wie lange dauert es, wenn 1 MB Daten vom Typ int aus dem Speicher gelesen und von der CPU akkumuliert werden?

Führen Sie eine einfache Berechnung durch. Der int-Typ in Java beträgt insgesamt 1024 * 1024/4 = 262144 Ganzzahlen Wir wissen, dass das Lesen von 1 Million Daten aus dem Speicher 250.000 Nanosekunden dauert. Zwischen beiden besteht zwar eine Lücke (diese Lücke ist natürlich nicht klein, 100.000 Nanosekunden reichen aus, damit die CPU fast 200.000 Anweisungen ausführen kann). immer noch in der gleichen Größenordnung. Ohne Caching-Mechanismus bedeutet dies jedoch, dass jede Zahl aus dem Speicher gelesen werden muss. In diesem Fall dauert es 100 Nanosekunden, bis die CPU den Speicher einmal liest, und 262144 Ganzzahlen werden aus dem Speicher in die CPU gelesen Die Berechnungszeit beträgt 262144
100+250000 = 26 464 400 Nanosekunden, was einen Unterschied in der Größenordnung darstellt. Und in Wirklichkeit können die meisten Computeraufgaben nicht einfach durch „Rechnen“ durch den Prozessor erledigt werden. Der Prozessor muss zumindest mit dem Speicher interagieren, wie z. B. das Lesen von Computerdaten, das Speichern von Computerergebnissen usw. Diese E/A-Operation ist es Im Grunde ist es unmöglich, sie zu beseitigen (es ist nicht möglich, sich bei der Erledigung aller Rechenaufgaben allein auf Register zu verlassen). Die Geschwindigkeiten der CPU und des Speichers waren in frühen Computern nahezu gleich, aber in modernen Computern übersteigt die Befehlsgeschwindigkeit der CPU die Zugriffsgeschwindigkeit des Speichers bei weitem, da zwischen dem Speichergerät des Computers eine Lücke von mehreren Größenordnungen besteht und der Rechengeschwindigkeit des Prozessors, moderne Computer Computersysteme müssen eine Cache-Schicht (Cache) mit einer Lese- und Schreibgeschwindigkeit hinzufügen, die möglichst nahe an der Betriebsgeschwindigkeit des Prozessors liegt, als Puffer zwischen dem Speicher und dem Prozessor: die benötigten Daten Der Vorgang wird in den Cache kopiert, sodass der Vorgang schnell ausgeführt werden kann. Wenn der Vorgang abgeschlossen ist, wird er aus dem Cache wieder mit dem Speicher synchronisiert, sodass der Prozessor nicht auf langsame Lese- und Schreibvorgänge im Speicher warten muss.

Wissenspunkte zur gleichzeitigen Programmierung für das Java-Thread-Lernen

Wissenspunkte zur gleichzeitigen Programmierung für das Java-Thread-Lernen In einem Computersystem ist das Register der L0-Level-Cache, gefolgt von L1, L2 und L3 (gefolgt von Speicher, lokaler Festplatte und Remote-Speicher). Der Cache-Speicherplatz weiter oben ist kleiner, die Geschwindigkeit ist höher und die Kosten sind höher; der Speicherplatz weiter unten ist größer, die Geschwindigkeit ist langsamer und die Kosten sind niedriger. Von oben nach unten kann jede Schicht als Cache der nächsten Schicht betrachtet werden, das heißt: Das L0-Register ist der Cache des L1-Cache der ersten Ebene, L1 ist der Cache der L2 und so weiter Jede Schicht stammt von der darunter liegenden Schicht, sodass die Daten jeder Schicht eine Teilmenge der Daten der nächsten Schicht sind.

Wissenspunkte zur gleichzeitigen Programmierung für das Java-Thread-Lernen Bei modernen CPUs sind im Allgemeinen L0, L1, L2 und L3 in die CPU integriert, und L1 ist außerdem in einen Datencache der ersten Ebene (Datencache, D-Cache, L1d) und einen unterteilt Befehlscache der ersten Ebene (Befehlscache, I-Cache, L1i), der jeweils zum Speichern von Daten und zum Ausführen der Befehlsdecodierung von Daten verwendet wird. Jeder Kern verfügt über eine unabhängige Recheneinheit, einen Controller, ein Register sowie einen L1- und L2-Cache, und dann teilen sich mehrere Kerne einer CPU die letzte Schicht des CPU-Cache L3.

2. Java Memory Model (JMM)

Aus abstrakter Sicht definiert JMM die abstrakte Beziehung zwischen Threads und dem Hauptspeicher: Gemeinsam genutzte Variablen zwischen Threads werden im Hauptspeicher (Hauptspeicher) gespeichert, und jeder Thread hat eine eigene Lokaler Speicher (Local Memory), der eine Kopie der gemeinsam genutzten Variablen speichert, die der Thread lesen/schreiben kann. Lokaler Speicher ist ein abstraktes Konzept von JMM und existiert nicht wirklich. Es umfasst Caches, Schreibpuffer, Register und andere Hardware- und Compiler-Optimierungen.

Wissenspunkte zur gleichzeitigen Programmierung für das Java-Thread-Lernen

Wissenspunkte zur gleichzeitigen Programmierung für das Java-Thread-Lernen

2.1 Sichtbarkeit

Sichtbarkeit bedeutet, dass, wenn mehrere Threads auf dieselbe Variable zugreifen und ein Thread den Wert der Variablen ändert, andere Threads den geänderten Wert sofort sehen können.
Da alle Operationen an Variablen durch Threads im Arbeitsspeicher ausgeführt werden müssen und Variablen im Hauptspeicher nicht direkt gelesen und geschrieben werden können, befinden sich die gemeinsam genutzten Variablen V zunächst in ihrem eigenen Arbeitsspeicher und werden dann mit dem Hauptspeicher synchronisiert. Allerdings wird es nicht rechtzeitig in den Hauptspeicher geleert, sondern es entsteht ein gewisser Zeitunterschied. Offensichtlich ist die Operation von Thread A an der Variablen V zu diesem Zeitpunkt für Thread B nicht mehr sichtbar.
Um das Problem der Sichtbarkeit gemeinsamer Objekte zu lösen, können wir das Schlüsselwort volatile oder lock verwenden.

2.2. Atomarität

​​​​ Atomizität: Das heißt, eine Operation oder mehrere Operationen werden entweder alle ausgeführt und der Ausführungsprozess wird durch keine Faktoren unterbrochen, oder sie werden überhaupt nicht ausgeführt.
Wir alle wissen, dass CPU-Ressourcen in Thread-Einheiten zugewiesen und zeitgesteuert aufgerufen werden. Das Betriebssystem ermöglicht die Ausführung eines Prozesses für einen kurzen Zeitraum, z. B. 50 Millisekunden wählt einen Prozess erneut aus (wir nennen es „Aufgabenwechsel“), diese 50 Millisekunden werden als „Zeitscheibe“ bezeichnet. Die meisten Aufgaben werden nach Ablauf des Zeitabschnitts gewechselt.
Warum verursacht der Thread-Wechsel Fehler? Da das Betriebssystem einen Taskwechsel durchführt, kann dieser nach der Ausführung eines CPU-Befehls erfolgen! Beachten Sie, dass es sich um eine CPU-Anweisung, CPU-Anweisung, CPU-Anweisung und nicht um eine Anweisung in einer Hochsprache handelt. Beispielsweise ist count++ in Java nur ein Satz, in höheren Sprachen erfordert eine Anweisung jedoch häufig mehrere CPU-Anweisungen, um ausgeführt zu werden. Tatsächlich enthält count++ mindestens drei CPU-Anweisungen! 那么线程切换为什么会带来 bug 呢?因为操作系统做任务切换,可以发生在任何一条 CPU 指令执行完!注意,是 CPU 指令,CPU 指令,CPU 指令,而不是高级语言里的一条语句。比如 count++,在 java 里就是一句话,但高级语言里一条语句往往需要多条 CPU 指令完成。其实 count++至少包含了三个 CPU 指令!

三、volatile 详解

3.1、volatile 特性

可以把对 volatile 变量的单个读/写,看成是使用同一个锁对这些单个读/写

3. Detaillierte Erklärung der flüchtigen Funktionen

Sie können einen einzelnen Lesen/Schreiben einer flüchtigen Variablen so betrachten, als würden Sie dieselbe Sperre zum Lesen/Schreiben dieser einzelnen Codes> verwenden ist synchronisiert

public class Volati {


    //    使用volatile 声明一个64位的long型变量
    volatile long i = 0L;//    单个volatile 变量的读
    public long getI() {
        return i;
    }//    单个volatile 变量的写
    public void setI(long i) {
        this.i = i;
    }//    复合(多个)volatile 变量的 读/写
    public void iCount(){
        i ++;
    }}

kann als folgender Code gesehen werden:

    public class VolaLikeSyn {
    
        //    使用 long 型变量
        long i = 0L;
        public synchronized long getI() {
            return i;
        }//     对单个的普通变量的读用同一个锁同步
        public synchronized void setI(long i) {
            this.i = i;
        }//    普通方法调用
        public void iCount(){
            long temp = getI();   // 调用已同步的读方法
            temp = temp + 1L;     // 普通写操作
            setI(temp);           // 调用已同步的写方法
        }}
  • Die flüchtige Variable selbst hat also die folgenden Eigenschaften:
  • Sichtbarkeit: Das Lesen einer flüchtigen Variablen kann immer gesehen werden. Der endgültige Schreibvorgang (von jedem Thread). ) zu dieser flüchtigen Variablen.
  • Atomizität

    : Das Lesen/Schreiben einer einzelnen flüchtigen Variablen ist atomar, aber zusammengesetzte Operationen wie volatile++ sind nicht atomar.

    Obwohl volatile sicherstellen kann, dass Variablen rechtzeitig nach der Ausführung in den Hauptspeicher geleert werden, hat Thread A für count++, eine nicht-atomare Situation mit mehreren Anweisungen, aufgrund des Threadwechsels gerade count=0 in den Arbeitsspeicher geladen , und Thread B hat gerade count=0 in den Arbeitsspeicher geladen. Dadurch sind die Ausführungsergebnisse von Thread A und B beide 1 und werden in den Hauptspeicher geschrieben Speicher ist immer noch 1, nicht 2
    • 3.2 Das Implementierungsprinzip von flüchtigen Schlüsseln. Wortmodifizierte Variablen haben das Präfix „lock:“.
    • Lock-Präfix, Lock ist keine Speicherbarriere, kann aber ähnliche Funktionen wie eine Speicherbarriere ausführen. Lock sperrt den CPU-Bus und den Cache, was als Sperre auf CPU-Befehlsebene verstanden werden kann.
    • Gleichzeitig schreibt dieser Befehl die Daten der aktuellen Prozessor-Cache-Zeile direkt in den Systemspeicher, und durch dieses Zurückschreiben in den Speicher werden die an dieser Adresse in anderen CPUs zwischengespeicherten Daten ungültig.

    4. Synchronisiertes Implementierungsprinzip

    Die Implementierung von Synchronized in JVM basiert auf dem Eingeben und Verlassen von Monitorobjekten, um Methodensynchronisation und Codeblocksynchronisation zu erreichen. Obwohl die spezifischen Implementierungsdetails unterschiedlich sind, können sie alle über gepaarte MonitorEnter implementiert werden und MonitorExit-Direktive zu erreichen.
    Bei synchronisierten Blöcken wird die MonitorEnter-Anweisung am Anfang des synchronisierten Codeblocks eingefügt, während die monitorExit-Anweisung am Ende der Methode und Ausnahme eingefügt wird. Die JVM garantiert, dass jeder MonitorEnter über einen entsprechenden MonitorExit verfügen muss. Wenn der Code diese Anweisung ausführt, versucht er im Allgemeinen, den Besitz des Objektmonitors zu erlangen, das heißt, er versucht, die Sperre des Objekts zu erhalten:

    1. Wenn die Eintragsnummer des Monitors 0 ist, tritt der Thread in den Monitor ein und setzt dann die Eintragsnummer auf 1, und der Thread wird Eigentümer des Monitors.
    2. Wenn der Thread den Monitor bereits belegt und einfach erneut eintritt, wird die Anzahl der Einträge im Monitor um 1 erhöht.
    3. Wenn andere Threads den Monitor bereits belegt haben, wechselt der Thread in den Blockierungsstatus, bis die Eintragsnummer des Monitors 0 ist, und versucht dann erneut, den Besitz des Monitors zu erlangen. Bei der Synchronisationsmethode wird den Dekompilierungsergebnissen der Synchronisationsmethode zufolge die Synchronisation der Methode nicht über die Anweisungen Monitorenter und Monitorexit implementiert. Im Vergleich zur normalen Methode verfügt der Konstantenpool über eine zusätzliche ACC_SYNCHRONIZED-Kennung.
      Die JVM implementiert die Methodensynchronisierung basierend auf dieser Kennung: Wenn die Methode aufgerufen wird, prüft die aufrufende Anweisung, ob das Zugriffsflag ACC_SYNCHRONIZED der Methode gesetzt ist. Wenn es gesetzt ist, ruft der Ausführungsthread zuerst den Monitor ab und führt ihn dann aus Nachdem die Methode erfolgreich erfasst wurde, wird der Monitor freigegeben, nachdem die Methode ausgeführt wurde. Während der Ausführung der Methode kann kein anderer Thread dasselbe Monitorobjekt erneut abrufen.

            Die von synchronisiert verwendete Sperre wird im Java-Objekt-Header gespeichert. Der Objekt-Header des Java-Objekts besteht aus zwei Teilen: Mark Word und Klass-Zeiger:

    1. Mark Word speichert Synchronisationsstatus, Logo, Hashcode, GC-Status. usw. Der
    2. klass-Zeiger speichert den Typzeiger des Objekts, der auf seine Klassenmetadaten zeigt. Darüber hinaus gibt es für Arrays auch Daten, die die Länge des Arrays aufzeichnen.

    Wissenspunkte zur gleichzeitigen Programmierung für das Java-Thread-Lernen

    Die Sperrinformationen sind im Markierungswort des Objekts vorhanden. Die Standarddaten in MarkWord dienen zum Speichern des HashCodes und anderer Informationen des Objekts.

    Wissenspunkte zur gleichzeitigen Programmierung für das Java-Thread-Lernen

    Aber es ändert sich, wenn sich der Betrieb des Objekts ändert.

    Wissenspunkte zur gleichzeitigen Programmierung für das Java-Thread-Lernen

    4.1. Beim Vergleich des obigen Bildes haben wir festgestellt, dass es insgesamt gibt Sperren Es gibt vier Zustände:

    kein Sperrzustand, voreingenommener Sperrzustand, leichter Sperrzustand und schwerer Sperrzustand

    , die mit der Konkurrenz allmählich eskalieren. Sperren können hochgestuft, aber nicht herabgestuft werden, um die Effizienz beim Erwerb und Freigeben von Sperren zu verbessern. 4.2. Voreingenommene Sperre

    Einführender Hintergrund

    : In den meisten Fällen gibt es bei Sperren nicht nur keine Multi-Thread-Konkurrenz, sondern sie werden immer mehrfach von demselben Thread erworben, um den Erwerb von Sperren für Threads zu verbilligen , voreingenommene Sperren werden eingeführt. Reduzieren Sie unnötige CAS-Operationen. „Die voreingenommene Sperre wirkt sich, wie der Name schon sagt, auf den ersten Besuch des Threads aus. Wenn ein Thread während des Vorgangs nur auf die synchrone Sperre zugreift, liegt kein Multithread-Streit vor, und der Thread muss nicht ausgelöst werden synchronisieren, reduzieren, reduzieren Einige CAS-Vorgänge zum Sperren/Entsperren (z. B. einige CAS-Vorgänge in Warteschlangen). In diesem Fall wird dem Thread eine Bias-Sperre hinzugefügt. Wenn andere Threads die Sperre während des Betriebs verhindern, wird der Thread, der die voreingenommene Sperre hält, angehalten, und die JVM entfernt die voreingenommene Sperre für ihn und stellt die Sperre auf eine Standard-Lightweight-Sperre wieder her. Es verbessert die Laufleistung des Programms weiter, indem es Synchronisierungsprimitive eliminiert, wenn keine Konkurrenz um Ressourcen besteht.
    Sehen Sie sich das Bild unten an, um den Prozess der Bias-Lock-Erfassung zu verstehen:

    Schritt 1. Besuchen Sie Mark Word, um zu sehen, ob das Bias-Lock-Flag auf 1 gesetzt ist und ob das Lock-Flag 01 ist. Bestätigen Sie dies es befindet sich in einem voreingenommenen Zustand. Wissenspunkte zur gleichzeitigen Programmierung für das Java-Thread-Lernen Schritt 2. Wenn es sich im vorgespannten Zustand befindet, testen Sie, ob die Thread-ID auf den aktuellen Thread verweist. Wenn ja, fahren Sie mit Schritt 5 fort, andernfalls fahren Sie mit Schritt 3 fort.

    Schritt 3. Wenn die Thread-ID nicht auf den aktuellen Thread verweist, konkurrieren Sie um die Sperre durch den CAS-Vorgang. Wenn der Wettbewerb erfolgreich ist, setzen Sie die Thread-ID in Mark Word auf die aktuelle Thread-ID und führen Sie dann 5 aus. Wenn der Wettbewerb fehlschlägt, führen Sie 4 aus.

    Schritt 4. Wenn es CAS nicht gelingt, die Bias-Sperre zu erlangen, bedeutet das, dass es Konkurrenz gibt. Beim Erreichen des globalen Sicherheitspunkts (Sicherheitspunkt) wird der Thread, der die Bias-Sperre erhält, angehalten, die Bias-Sperre wird auf eine leichte Sperre aktualisiert und dann führt der am Sicherheitspunkt blockierte Thread den Synchronisationscode weiterhin aus. (Das Aufheben der Bias-Sperre führt dazu, dass das Wort gestoppt wird.)
    Schritt 5. Führen Sie den Synchronisierungscode aus.


    Bias Lock Release:

    Die Aufhebung der Voreingenommenheitssperre wird oben im vierten Schritt erwähnt. Nur wenn andere Threads versuchen, um die Bias-Sperre zu konkurrieren, gibt der Thread, der die Bias-Sperre hält, die Bias-Sperre frei, und der Thread gibt die Bias-Sperre nicht aktiv frei. Um die voreingenommene Sperre aufzuheben, müssen Sie auf den globalen Sicherheitspunkt warten (zu diesem Zeitpunkt wird kein Bytecode ausgeführt). Zuerst wird der Thread angehalten, der die voreingenommene Sperre besitzt, und feststellen, ob sich das Sperrobjekt in einem gesperrten Zustand befindet , und stellen Sie dann die voreingenommene Sperre auf den vorherigen Zustand zurück, nachdem Sie die voreingenommene Sperre aufgehoben haben. Der Status der Sperre (Flag-Bit ist „01“) oder der Lightweight-Sperre (Flag-Bit ist „00“).

    Anwendbare Szenarien für voreingenommene Sperren:

    Es gibt immer nur einen Thread, der den Synchronisationsblock ausführt, bevor er die Ausführung beendet und die Sperre aufhebt. Es wird verwendet, wenn kein anderer Thread vorhanden ist Sobald die Konkurrenz auf eine leichte Sperre aktualisiert wird, muss die voreingenommene Sperre aufgehoben werden Bei Sperren führt die voreingenommene Sperre viele zusätzliche Vorgänge aus. Insbesondere beim Aufheben der voreingenommenen Sperre führt dies zum Eintritt in den sicheren Punkt und zu einer Leistungseinbuße .

    jvm Vorspannungssperre ein-/ausschalten

    Vorspannungssperre einschalten: -XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0 Vorspannungssperre ausschalten: -XX:-UseBiasedLocking

    4.3, leichte Sperre

    ​​​​ Von der Bias-Sperre aktualisiert, wird die Bias-Sperre ausgeführt, wenn ein Thread dem Sperrenkonflikt beitritt.

    Lightweight-Sperre Der Sperrprozess:

      Wenn der Code in den Synchronisationsblock eintritt und der Sperrstatus des Synchronisationsobjekts entsperrt ist und keine Voreingenommenheit zulässig ist (das Sperrflag ist „01“, ob es sich um eine voreingenommene Sperre handelt, ist „0“), wird die virtuelle Maschine zuerst ausgeführt Erstellen Sie im Stapelrahmen des aktuellen Threads einen Bereich namens Lock Record, der zum Speichern einer Kopie des aktuellen Markierungsworts des Sperrobjekts verwendet wird, das offiziell als Displaced Mark Word bezeichnet wird.
    1. Kopieren Sie das Markierungswort im Objektheader in den Sperrdatensatz.
    2. Nachdem der Kopiervorgang erfolgreich war, versucht die virtuelle Maschine mithilfe der CAS-Operation, das Markierungswort des Objekts auf einen Zeiger auf den Sperrdatensatz zu aktualisieren und den Besitzerzeiger im Sperrdatensatz auf das Objektmarkierungswort zu verweisen. Wenn das Update erfolgreich ist, fahren Sie mit Schritt 4 fort, andernfalls fahren Sie mit Schritt 5 fort.
    3. Wenn dieser Aktualisierungsvorgang erfolgreich ist, besitzt dieser Thread die Sperre des Objekts und das Sperrflag des Objekts Mark Word wird auf „00“ gesetzt, was bedeutet, dass sich dieses Objekt in einem leichten Sperrzustand befindet.
    4. Wenn dies der Fall ist Wenn der Aktualisierungsvorgang fehlschlägt, prüft die virtuelle Maschine zunächst, ob das Markierungswort des Objekts auf den Stapelrahmen des aktuellen Threads verweist. Wenn dies der Fall ist, bedeutet dies, dass der aktuelle Thread bereits die Sperre dieses Objekts besitzt, und kann dies dann tun Geben Sie direkt den Synchronisationsblock ein, um die Ausführung fortzusetzen. Andernfalls bedeutet dies, dass mehrere Threads um die Sperre konkurrieren, sich dann drehen und auf die Sperre warten und das Sperrobjekt nach einer bestimmten Anzahl von Malen nicht abgerufen wurde. Der Heavyweight-Thread-Zeiger zeigt auf den konkurrierenden Thread, und der konkurrierende Thread blockiert ebenfalls und wartet darauf, dass der Lightweight-Thread die Sperre aufhebt und ihn aufweckt. Der Statuswert des Sperrflags ändert sich auf „10“. Was im Markierungswort gespeichert ist, ist der Zeiger auf die Schwergewichtssperre (Mutex), und die nachfolgenden Threads, die auf die Sperre warten, treten ebenfalls in den Blockierungsstatus ein.
    4.3.1. Spin-Lock-Prinzip

    Das Prinzip des Spin-Lock ist sehr einfach. Wenn der Thread, der die Sperre hält, die Sperrressource in kurzer Zeit freigeben kann, müssen die Threads, die auf die Sperre warten, keinen Kernel ausführen Status und Wechsel zwischen Benutzermodi gehen in einen blockierenden und suspendierten Zustand über. Sie müssen nur warten (drehen) und die Sperre sofort erhalten, nachdem der Thread, der die Sperre hält, die Sperre aufhebt. Dadurch wird der Aufwand für das Umschalten zwischen Benutzer-Threads und dem Kernel vermieden.

                Aber das Thread-Spinning erfordert CPU-Verbrauch, um es ganz klar auszudrücken: Der Thread kann die CPU nicht immer für nutzlose Arbeit beanspruchen, daher müssen Sie eine maximale Spin-Wartezeit festlegen.
    Wenn die Ausführungszeit des Threads, der die Sperre hält, die maximale Spin-Wartezeit überschreitet und die Sperre nicht aufgehoben wird, können andere Threads, die um die Sperre konkurrieren, die Sperre immer noch nicht innerhalb der maximalen Wartezeit erhalten Der konkurrierende Thread stoppt sich selbst.

    4.3.2. Vor- und Nachteile von Spin-Locks

    Spin-Locks reduzieren die Thread-Blockierung so weit wie möglich, was die Leistung von Codeblöcken erheblich verbessert, die keine starke Konkurrenz um Locks haben und eine sehr kurze Lock-Zeit beanspruchen Die Spin-Kosten sind geringer als die Kosten für das Blockieren und Anhalten von Threads.

    Wenn jedoch die Konkurrenz um die Sperre groß ist oder der Thread, der die Sperre hält, die Sperre lange Zeit belegen muss, um den Synchronisationsblock auszuführen, ist die Verwendung der Spin-Sperre zu diesem Zeitpunkt nicht geeignet, da die Spin-Sperre immer aktiviert ist Beansprucht die CPU für nutzlose Arbeit, bevor die Sperre erlangt wird. Der Verbrauch durch Thread-Spinning ist größer als der Verbrauch durch Thread-Blockierungs- und Suspendierungsvorgänge der CPU.

    4.3.3. Zeitschwelle für die Spin-Sperre

    Der Zweck der Spin-Sperre besteht darin, die CPU-Ressourcen zu belegen, ohne sie freizugeben, und zu warten, bis die Sperre erworben wird, um sie sofort zu verarbeiten. Aber wie wählt man die Ausführungszeit des Spins? Wenn die Spin-Ausführungszeit zu lang ist, befindet sich eine große Anzahl von Threads im Spin-Zustand und belegt CPU-Ressourcen, was sich auf die Leistung des Gesamtsystems auswirkt. Daher ist die Anzahl der Spins wichtig. JVM wählt die Anzahl der Drehungen aus, JDK1.5 ist standardmäßig auf 10 eingestellt, und in 1.6 wurden adaptive Spin-Sperren eingeführt. Die adaptive Spin-Sperre bedeutet, dass die Spin-Zeit nicht festgelegt ist, sondern von der vorherigen Zeit zur vorherigen Zeit in der vorherigen Zeit Die vorherige Zeit wird durch die Spinzeit derselben Sperre und den Status des Sperrenbesitzers bestimmt. Grundsätzlich wird davon ausgegangen, dass die Zeit des Kontextwechsels eines Threads die beste ist.

    In JDK1.6, -XX:+UseSpinning aktiviert die Spin-Sperre; nach JDK1.7 wird dieser Parameter entfernt und von jvm gesteuert;

    Wissenspunkte zur gleichzeitigen Programmierung für das Java-Thread-Lernen

    4.3.4, Vergleich verschiedener Sperren

    Wissenspunkte zur gleichzeitigen Programmierung für das Java-Thread-Lernen

    Empfohlene Studie: „

    Java-Video-Tutorial

    Das obige ist der detaillierte Inhalt vonWissenspunkte zur gleichzeitigen Programmierung für das Java-Thread-Lernen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Dieser Artikel ist reproduziert unter:csdn.net. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen