Mindmap-Galerie Wissenskarte des Java-Parallelitätssystems (gleichzeitige Programmierung) Mindmap
Dies ist eine Mindmap zur Wissenskarte des Java-Parallelitätssystems (gleichzeitige Programmierung), einschließlich Blockierungswarteschlange, Parallelitätsgrundlagen, Sperren, JAVA-Speichermodell und anderen Aspekten des Wissens.
Bearbeitet um 2023-11-06 19:35:07Einhundert Jahre Einsamkeit ist das Meisterwerk von Gabriel Garcia Marquez. Die Lektüre dieses Buches beginnt mit der Klärung der Beziehungen zwischen den Figuren. Im Mittelpunkt steht die Familie Buendía, deren Wohlstand und Niedergang, interne Beziehungen und politische Kämpfe, Selbstvermischung und Wiedergeburt im Laufe von hundert Jahren erzählt werden.
Einhundert Jahre Einsamkeit ist das Meisterwerk von Gabriel Garcia Marquez. Die Lektüre dieses Buches beginnt mit der Klärung der Beziehungen zwischen den Figuren. Im Mittelpunkt steht die Familie Buendía, deren Wohlstand und Niedergang, interne Beziehungen und politische Kämpfe, Selbstvermischung und Wiedergeburt im Laufe von hundert Jahren erzählt werden.
Projektmanagement ist der Prozess der Anwendung von Fachwissen, Fähigkeiten, Werkzeugen und Methoden auf die Projektaktivitäten, so dass das Projekt die festgelegten Anforderungen und Erwartungen im Rahmen der begrenzten Ressourcen erreichen oder übertreffen kann. Dieses Diagramm bietet einen umfassenden Überblick über die 8 Komponenten des Projektmanagementprozesses und kann als generische Vorlage verwendet werden.
Einhundert Jahre Einsamkeit ist das Meisterwerk von Gabriel Garcia Marquez. Die Lektüre dieses Buches beginnt mit der Klärung der Beziehungen zwischen den Figuren. Im Mittelpunkt steht die Familie Buendía, deren Wohlstand und Niedergang, interne Beziehungen und politische Kämpfe, Selbstvermischung und Wiedergeburt im Laufe von hundert Jahren erzählt werden.
Einhundert Jahre Einsamkeit ist das Meisterwerk von Gabriel Garcia Marquez. Die Lektüre dieses Buches beginnt mit der Klärung der Beziehungen zwischen den Figuren. Im Mittelpunkt steht die Familie Buendía, deren Wohlstand und Niedergang, interne Beziehungen und politische Kämpfe, Selbstvermischung und Wiedergeburt im Laufe von hundert Jahren erzählt werden.
Projektmanagement ist der Prozess der Anwendung von Fachwissen, Fähigkeiten, Werkzeugen und Methoden auf die Projektaktivitäten, so dass das Projekt die festgelegten Anforderungen und Erwartungen im Rahmen der begrenzten Ressourcen erreichen oder übertreffen kann. Dieses Diagramm bietet einen umfassenden Überblick über die 8 Komponenten des Projektmanagementprozesses und kann als generische Vorlage verwendet werden.
Gleichzeitige Programmierung
Java-Speichermodell (JMM)
Thread-Kommunikationsmechanismus
Speicherfreigabe
Java-Einführung
Nachrichtenübermittlung
Speichermodell
Neu anordnen
Für die Leistung des Programms ordnen der Prozessor und der Compiler das Programm neu.
Zustand
Die Ergebnisse der Programmausführung können in einer Single-Threaded-Umgebung nicht geändert werden.
Eine Neuordnung ist nicht zulässig, wenn Datenabhängigkeiten bestehen
Frage
Eine Neuordnung kann in einer Multithread-Umgebung zu unsicheren Daten führen
sequentielle Konsistenz
Theoretisches Referenzmodell in einer Multithread-Umgebung
Bietet starke Garantien für die Speichersichtbarkeit von Programmen
charakteristisch
Alle Vorgänge in einem Thread müssen in der Reihenfolge des Programms ausgeführt werden
Alle Threads können nur eine einzige Reihenfolge der Operationsausführung sehen, unabhängig davon, ob das Programm synchronisiert ist oder nicht
Jede Operation muss atomar ausgeführt werden und für alle Threads sofort sichtbar sein
passiert-vorher
Die Kerntheorie in JMM gewährleistet die Sichtbarkeit des Speichers
Wenn in JMM die Ergebnisse einer Operation für eine andere Operation sichtbar sein müssen, muss zwischen den beiden Operationen eine Vorhergehensbeziehung bestehen.
Theorie
Wenn eine Operation vor einer anderen Operation ausgeführt wird, sind die Ausführungsergebnisse der ersten Operation für die zweite Operation sichtbar und die Ausführungsreihenfolge der ersten Operation ist vor der zweiten Operation.
Das Vorhandensein einer „Passiert-vorher“-Beziehung zwischen zwei Operationen bedeutet nicht, dass sie in der durch das „Passiert-vorher“-Prinzip festgelegten Reihenfolge ausgeführt werden müssen. Wenn das Ausführungsergebnis nach der Neuordnung mit dem Ausführungsergebnis gemäß der Vorher-Beziehung übereinstimmt, ist diese Neuordnung nicht illegal.
als-ob-seriell
Alle Vorgänge können zur Optimierung neu angeordnet werden, Sie müssen jedoch sicherstellen, dass die Ergebnisse der Neuordnung nicht geändert werden können.
synchronisiert
Synchronisation, Schwergewichtssperre
Prinzip
Durch die Synchronisierung kann sichergestellt werden, dass beim Ausführen einer Methode oder eines Codeblocks jeweils nur eine Methode in den kritischen Abschnitt gelangen kann. Außerdem kann die Speichersichtbarkeit gemeinsam genutzter Variablen sichergestellt werden.
Objekt sperren
Bei der normalen Synchronisationsmethode ist die Sperre das aktuelle Instanzobjekt
Statische Synchronisationsmethode, die Sperre ist das Klassenobjekt der aktuellen Klasse
Synchronisierter Methodenblock, die Sperre ist das Objekt innerhalb der Klammern
Implementierungsmechanismus
Java-Objektheader
Die synchronisierte Sperre wird im Java-Objektheader gespeichert.
Enthält zwei Datenteile
Wort markieren (Feld markieren)
Mark Word ist als nicht feste Datenstruktur konzipiert, um so viele Daten wie möglich auf sehr kleinem Raum zu speichern. Je nach Status des Objekts wird der eigene Speicherplatz wiederverwendet.
enthalten
Hash-Code (HashCode), Alter der GC-Generierung, Sperrstatus-Flag, vom Thread gehaltene Sperre, voreingenommene Thread-ID, voreingenommener Zeitstempel
Klassenzeiger (Typzeiger)
Monitor
Eigentümer
Anfänglich bedeutet NULL, dass derzeit kein Thread Eigentümer der Sperre ist. Wenn die Sperre aufgehoben wird, wird die eindeutige Kennung des Threads gespeichert.
Sperroptimierung
Spin-Lock
Der Thread wartet eine gewisse Zeit und wird nicht sofort angehalten, um zu sehen, ob der Thread, der die Sperre hält, die Sperre bald freigibt (zyklische Methode).
Die Anzahl der Spin-Wörter ist schwer zu kontrollieren (-XX:preBlockSpin)
Existenztheorie: Threads werden häufig mit hoher Belastung angehalten und aufwachen. Es kann davon ausgegangen werden, dass jeder Thread die Sperre für kurze Zeit hält und der Gewinn den Gewinn überwiegt, nachdem der Thread angehalten und dann aufgeweckt wurde.
Mangel
Die Anzahl der Drehungen kann nicht ermittelt werden
Adaptive Spin-Sperre
Die Anzahl der Drehungen ist nicht mehr festgelegt. Sie wird durch die vorherige Drehungszeit am selben Schloss und den Status des Schlosseigentümers bestimmt.
Wenn die Drehung erfolgreich ist, kann die Anzahl der Drehungen erhöht werden. Wenn die Erlangung der Sperre häufig fehlschlägt, wird die Anzahl der Drehungen verringert.
Sperrenbeseitigung
Wenn kein Datenwettbewerb besteht, eliminiert die JVM den Sperrmechanismus
Urteile basieren
Variablenflucht
Schlossaufrauung
Verbinden Sie mehrere aufeinanderfolgende Verriegelungs- und Entriegelungsvorgänge miteinander, um ein größeres Schloss zu erhalten. Zum Beispiel das Erlangen einer Sperre innerhalb einer for-Schleife
leichtes Schloss
Reduzieren Sie den durch herkömmliche Schwergewichtssperren verursachten Leistungsverbrauch mithilfe von Betriebssystem-Mutexes ohne Multithread-Konkurrenz.
Sperren über CAS erwerben und freigeben
Leistungsbasis
Bei den meisten Schlössern wird es während des gesamten Lebenszyklus keine Konkurrenz geben.
Mangel
In einer Multithread-Umgebung ist die Betriebseffizienz langsamer als die schwerer Sperren.
Vorspannungssperre
Um unnötige, leichte Sperrausführungspfade ohne Multithread-Konkurrenz zu minimieren
Vermeiden Sie unnötige CAS-Operationen so weit wie möglich. Wenn die Konkurrenzsperre fehlschlägt, rüsten Sie auf eine leichte Sperre auf.
flüchtig
charakteristisch
Sichtbarkeit der flüchtigen Variable: Beim Lesen einer flüchtigen Variable können Sie immer den endgültigen Schreibvorgang für diese Variable sehen.
flüchtige Atomizität: flüchtig ist atomar für einen einzelnen Lese-/Schreibvorgang (32 Bit lang, doppelt), außer bei zusammengesetzten Operationen wie i;
Implementierungsmechanismus
Gedächtnisbarriere
Gedächtnissemantik
Beim Schreiben einer flüchtigen Variablen aktualisiert JMM sofort den Wert der gemeinsam genutzten Variablen im lokalen Speicher, der dem Thread im Hauptspeicher entspricht.
Beim Lesen einer flüchtigen Variablen setzt JMM den dem Thread entsprechenden lokalen Speicher auf ungültig und liest die gemeinsam genutzte Variable direkt aus dem Hauptspeicher.
Semantik des Betriebssystems
Hauptspeicher, Cache (privater Thread) Cache konsistent?
Lösung
Durch Hinzufügen von LOCK# zum Bus
Über Cache-Kohärenzprotokoll (MESI-Protokoll)
Speichermodell
Neu anordnen
passiert-vorher
DCL
Singleton-Muster
DCL
Neu anordnen
passiert-beofre
Lösung
flüchtige Lösung
Nachbestellung deaktivieren
Lösung basierend auf Klasseninitialisierung
Verwenden Sie den Classloder-Mechanismus, um sicherzustellen, dass beim Initialisieren der Instanz nur ein Thread vorhanden ist. Die JVM erhält während der Klasseninitialisierungsphase eine Sperre. Diese Sperre kann die Initialisierung derselben Klasse durch mehrere Threads synchronisieren.
Grundlagen der Parallelität
AQS
AbstractQueuedSynchronizer, Synchronizer, implementiert die Kernkomponenten von JUC
Viele detaillierte Probleme bei der Implementierung von Synchronisierern in Unterklassen wurden gelöst, z. B. das Abrufen des Synchronisierungsstatus und der FIFO-Synchronisierungswarteschlange.
Mithilfe des Vorlagenmethodenmusters implementiert AQS eine große Anzahl gängiger Methoden, und Unterklassen implementieren ihre abstrakten Methoden durch Vererbung, um den Synchronisationsstatus zu verwalten.
CLH-Synchronisierungswarteschlange
AQS ist auf die bidirektionale FIFO-Warteschlange angewiesen, um das Verwaltungsproblem des Synchronisationsstatus zu lösen
Der erste Knoten wacht auf und wartet darauf, dass die Warteschlange am Ende der CLH-Synchronisationswarteschlange hinzugefügt wird.
Synchrone Zustandserfassung und -freigabe
Exklusiv
Holen Sie sich die Sperre
Synchronisierungsstatus abrufen: erwerben
AcquireInterruptously: AcquireInterruptously
Timeout-Erfassung: tryAcquireNanos
Sperre lösen
freigeben
geteilt
Holen Sie sich die Sperre
erwerbenGeteilt
Sperre lösen
releaseShared
Threadblockierung und Aufwachen
Wenn ein Thread die Sperre erhält, müssen andere Threads blockieren, wenn der Thread die Sperre aufhebt, ist AQS dafür verantwortlich, den Thread aufzuwecken.
LockSupport
Ist das grundlegende Thread-Blockierungsprimitiv, das zum Erstellen von Sperren und anderen Synchronisierungsklassen verwendet wird
Jeder Thread, der LockSupport verwendet, ist mit einer Berechtigung verknüpft. Wenn die Berechtigung verfügbar ist und im Prozess verwendet werden kann, wird der Aufruf von park() sofort zurückgegeben, andernfalls kann es zu einer Blockierung kommen. Wenn die Lizenz noch nicht verfügbar ist, können Sie unpark anrufen, um sie verfügbar zu machen
park(), unpark()
CAS
Compare And Swap, der Kern und die grundlegendste Theorie des gesamten JUC-Systems
Speicherwert V, alter erwarteter Wert A und zu aktualisierender Wert B. Nur wenn der Wert von Speicherwert V gleich dem alten erwarteten Wert A ist, wird der Wert von Speicherwert V in B geändert, andernfalls nichts getan werden.
In native gibt es vier Parameter
Defekt
Zykluszeit zu lang
Es kann garantiert werden, dass nur eine gemeinsam genutzte Variable atomar betrieben wird
ABA-Problem
Lösung
Versionsnummer
AtomicStampedReference
Sperren
ReentrantLock
Die Wiedereintrittssperre ist ein rekursiver, nicht blockierender Synchronisationsmechanismus
Ein leistungsfähigerer und flexiblerer Sperrmechanismus als der synchronisierte, der die Wahrscheinlichkeit eines Deadlocks verringern kann.
Unterteilt in faire Sperre und unfaire Sperre
Die unterste Ebene wird mithilfe von AQS implementiert und erbt AQS durch interne Synchronisierung.
ReentrantReadWriteLock
Lese-/Schreibsperre, zwei Sperren: gemeinsame Sperre: Lesesperre, exklusive Sperre: Schreibsperre
Unterstützt Fairness, Unfairness, Wiedereintritt und Sperrverschlechterung
Herabstufung der Sperre: Entsprechend der Reihenfolge des Erwerbs einer Schreibsperre, des Erwerbs einer Lesesperre und der Freigabe einer Schreibsperre kann die Schreibsperre auf eine Lesesperre herabgestuft werden.
Zustand
Lock stellt eine Bedingung bereit, die detaillierter und flexibler für Thread-Warte- und Aufweckvorgänge ist.
Eine Bedingungswarteschlange wird intern gepflegt. Wenn der aktuelle Thread die Methode „await()“ aufruft, wird aus dem aktuellen Thread ein Knoten (Node) erstellt und am Ende der Warteschlange hinzugefügt.
Parallelitätstools
CyclicBarrier
Dadurch kann eine Gruppe von Threads aufeinander warten, bis ein gemeinsamer Barrierepunkt erreicht ist
Laienhaft ausgedrückt: Lassen Sie eine Gruppe von Threads blockieren, wenn sie eine Barriere erreichen. Die Barriere öffnet sich erst, wenn der letzte Thread die Barriere erreicht, und alle von der Barriere abgefangenen Threads funktionieren weiter.
Die unterste Ebene wird mithilfe der ReentrantLock-Bedingung implementiert
Anwendungsszenarien
Der Vorgang des Zusammenführens von Multi-Thread-Ergebnissen wird verwendet, um Daten in mehreren Threads zu berechnen und schließlich die Berechnungsergebnisse zusammenzuführen.
CountDownLatch
Dadurch können ein oder mehrere Threads warten, bis eine Reihe von Vorgängen, die in anderen Threads ausgeführt werden, abgeschlossen sind.
Initialisiert CountDownLatch mit der angegebenen Anzahl. Da die Methode „countDown()“ aufgerufen wird, blockiert die Methode „await“, bis der aktuelle Zählerstand Null erreicht. Anschließend werden alle wartenden Threads freigegeben und alle nachfolgenden Aufrufe von waiting kehren sofort zurück. Dieses Verhalten tritt nur einmal auf – der Zähler kann nicht zurückgesetzt werden. Wenn Sie den Zähler zurücksetzen müssen, sollten Sie die Verwendung eines CyclicBarrier in Betracht ziehen.
Unterschied zu CyclicBarrier
Die Funktion von CountDownLatch besteht darin, 1 oder N Threads darauf zu warten, dass andere Threads die Ausführung abschließen, während CyclicBarrier es N Threads ermöglicht, aufeinander zu warten.
Der Zähler von CountDownLatch kann nicht zurückgesetzt werden; der Zähler von CyclicBarrier kann zurückgesetzt und verwendet werden, daher wird er als zyklische Barriere bezeichnet.
Intern implementiert mithilfe gemeinsamer Sperren
Semaphor
Signal
Ein Zähler, der den Zugriff auf mehrere gemeinsam genutzte Ressourcen steuert
Konzeptionell verwaltet ein Semaphor einen Berechtigungssatz. Bei Bedarf blockiert jeder acquire(), bis die Berechtigung verfügbar ist, und ruft dann die Berechtigung ab. Jedes release() fügt eine Berechtigung hinzu und gibt möglicherweise einen blockierenden Getter frei. Anstatt jedoch das eigentliche Lizenzobjekt zu verwenden, zählt Semaphore lediglich die Anzahl der verfügbaren Lizenzen und ergreift entsprechende Maßnahmen
Der Semaphor Semaphor ist eine nicht negative Ganzzahl (>=1). Wenn ein Thread auf eine gemeinsam genutzte Ressource zugreifen möchte, muss er zuerst die Semaphore abrufen. Wenn Semaphore > 0, muss die Ressource abgerufen und Semaphore auf 1 gesetzt werden. Wenn der Semaphore-Wert = 0 ist, bedeutet dies, dass alle gemeinsam genutzten Ressourcen von anderen Threads belegt wurden und der Thread darauf warten muss, dass andere Threads die Ressourcen freigeben. Wenn der Thread die Ressource freigibt, ist das Semaphor 1
Anwendungsszenarien
Wird häufig verwendet, um die Anzahl der Threads zu begrenzen, die auf bestimmte Ressourcen (physisch oder logisch) zugreifen können.
Intern implementiert mithilfe gemeinsamer Sperren
Austauscher
Ein Synchronisationspunkt für Threads, die Elemente in einem Paar koppeln und austauschen können
Ermöglicht den Datenaustausch zwischen gleichzeitigen Aufgaben. Insbesondere ermöglicht die Exchanger-Klasse die Definition von Synchronisationspunkten zwischen zwei Threads. Wenn beide Threads den Synchronisationspunkt erreichen, tauschen sie Datenstrukturen aus, sodass die Datenstruktur des ersten Threads in den zweiten Thread und die Datenstruktur des zweiten Threads in den ersten Thread geht
andere
ThreadLocal
Eine Lösung für das Problem von Mitgliedsvariablen in einer Multithread-Umgebung, hat jedoch nichts mit der Thread-Synchronisation zu tun. Die Idee besteht darin, für jeden Thread eine separate Kopie der Variablen zu erstellen, sodass jeder Thread unabhängig seine eigene Kopie der Variablen ändern kann, ohne die entsprechenden Kopien anderer Threads zu beeinträchtigen.
ThreadLocal wird nicht zur Lösung des Problems gemeinsam genutzter Variablen verwendet und existiert auch nicht zur Koordinierung der Thread-Synchronisation, sondern ist ein Mechanismus, der eingeführt wurde, um jedem Thread die Handhabung seines eigenen Status zu erleichtern.
vier Methoden
get(): Gibt den Wert in der aktuellen Thread-Kopie dieser Thread-lokalen Variablen zurück
initialValue(): Gibt den „Anfangswert“ des aktuellen Threads für diese lokale Thread-Variable zurück
Remove(): Entfernen Sie den Wert dieser lokalen Thread-Variablen im aktuellen Thread
set(T-Wert): Setzt den Wert in der aktuellen Thread-Kopie dieser Thread-lokalen Variablen auf den angegebenen Wert
ThreadLocalMap
Der Schlüssel zur Implementierung des Thread-Isolationsmechanismus
Jeder Thread enthält eine Mitgliedsvariable vom Typ ThreadLocal.ThreadLocalMap, die zum Speichern einer Kopie der tatsächlichen ThreadLocal-Variablen verwendet wird.
Stellt eine Methode zum Speichern einer Kopie der Variablen jedes Threads mithilfe von Schlüssel-Wert-Paaren bereit. Der Schlüssel ist das aktuelle ThreadLocal-Objekt und der Wert ist die Variablenkopie des entsprechenden Threads.
Seien Sie vorsichtig
Die ThreadLocal-Instanz selbst speichert keinen Wert, sondern stellt lediglich einen Schlüssel bereit, um eine Kopie des Werts im aktuellen Thread zu finden.
Es handelt sich um ThreadLocal, das in Thread enthalten ist, nicht um Thread, das in ThreadLocal enthalten ist
Speicherleckproblem
ThreadLocalMap
Der Schlüssel ist ein schwacher Referenzwert, der eine starke Referenz ist und nicht recycelt werden kann
Remove() explizit aufrufen
Fork/Join
Ein Framework zum parallelen Ausführen von Aufgaben ist ein Framework, das große Aufgaben in mehrere kleine Aufgaben unterteilt und schließlich die Ergebnisse jeder kleinen Aufgabe zusammenfasst, um die Ergebnisse der großen Aufgabe zu erhalten.
Hauptidee
„Partition“
Fork zerlegt Aufgaben und Join sammelt Daten
Arbeitsplatzdiebstahl
Ein Thread stiehlt Aufgaben aus anderen Warteschlangen zur Ausführung
Der Thread, der den Block ausführt, hilft dem langsamen Thread bei der Ausführung der Aufgabe und verbessert die Effizienz der gesamten Aufgabe.
Die Warteschlange sollte eine bidirektionale Warteschlange sein
Kernklasse
ForkJoinPool
Thread-Pool zum Ausführen von Aufgaben
ForkJoinTask
Stellt Aufgaben dar, Aufgabenabstraktion für ForkJoinPool
ForkJoinWorkerThread
Arbeitsthread, der Aufgaben ausführt
Gleichzeitige Java-Sammlungen
ConcurrentHashMap
CAS Synchronized gewährleistet die Sicherheit gleichzeitiger Aktualisierungen. Die unterste Ebene verwendet eine Array-verknüpfte Listen-/Rot-Schwarz-Baum-Speicherstruktur.
Wichtige innere Klassen
Knoten
Schlüssel-Wert-Schlüssel-Wert-Paar
TreeNode
Rot-schwarzer Baumknoten
TreeBin
Es entspricht einem rot-schwarzen Baum. Seine Konstruktionsmethode ist eigentlich der Prozess der Konstruktion eines rot-schwarzen Baums.
ForwardingNode
Hilfsknoten, der für den ConcurrentHashMap-Erweiterungsvorgang verwendet wird
sizeCtl
Steueridentifikator, der zur Steuerung von Tabelleninitialisierungs- und -erweiterungsvorgängen verwendet wird
Bedeutung
Eine negative Zahl zeigt an, dass Initialisierungs- oder Erweiterungsvorgänge ausgeführt werden.
-1 bedeutet Initialisierung
-N gibt an, dass N-1 Threads Erweiterungsvorgänge durchlaufen
Eine positive Zahl oder 0 zeigt an, dass die Hash-Tabelle nicht initialisiert wurde. Dieser Wert gibt die Größe der Initialisierung oder nächsten Erweiterung an.
Wichtige Operationen
initTable
ConcurrentHashMap-Initialisierungsmethode
Am Initialisierungsprozess kann nur ein Thread teilnehmen, andere Threads müssen angehalten werden
Der Konstruktor führt den Initialisierungsprozess nicht durch. Die Initialisierung wird tatsächlich durch die Put-Operation ausgelöst.
Schritt
sizeCtl < 0 bedeutet, dass die Initialisierung läuft und der Thread angehalten ist
Der Thread erhält die Initialisierungsqualifikation (CAS(SIZECTL, sc, -1)), um den Initialisierungsprozess durchzuführen
Stellen Sie nach Abschluss des Initialisierungsschritts sizeCtl = 0,75 * n (den nächsten Erweiterungsschwellenwert) ein und geben Sie die Größe der nächsten Erweiterung an
setzen
Hauptidee
Berechnen Sie die Position des in die Tabelle eingefügten Knotens basierend auf dem Hash-Wert. Wenn die Position leer ist, fügen Sie sie direkt ein, andernfalls fügen Sie sie in eine verknüpfte Liste oder einen Baum ein.
Die reale Situation ist komplizierter
Schritt
Die Tabelle ist null und der Thread tritt in den Initialisierungsschritt ein. Wenn andere Threads initialisiert werden, bleibt der Thread hängen.
Wenn die aktuell eingefügte i-Position null ist, bedeutet dies, dass diese Position zum ersten Mal eingefügt wird. Wenn das Einfügen erfolgreich ist, wird addCount aufgerufen, um zu bestimmen, ob eine Erweiterung erforderlich ist. Wenn das Einfügen fehlschlägt, setzen Sie den Abgleich fort (drehen).
Wenn der Hash des Knotens == MOVED (-1) ist, bedeutet dies, dass ein Thread erweitert wird und in den Erweiterungsprozess eintritt.
In anderen Fällen werden Knoten gemäß der verknüpften Liste oder der rot-schwarzen Baumstruktur eingefügt, dieser Vorgang erfordert jedoch eine Sperre (synchronisiert).
erhalten
Schritt
table==null;null zurückgeben
Von verknüpfter Liste/rot-schwarzem Baumknoten abrufen
Erweiterung
Multithread-Erweiterung
Schritt
Erstellen Sie eine nextTable, deren Größe doppelt so groß ist wie die ursprüngliche Größe. Dieser Schritt wird in einer Single-Thread-Umgebung ausgeführt.
Kopieren Sie den Inhalt der Originaltabelle nach nextTable. Dieser Schritt ermöglicht Multithread-Operationen.
Der Prozess der Konvertierung einer verknüpften Liste in einen rot-schwarzen Baum
Erreicht die Anzahl der Elemente in der verknüpften Liste den Schwellenwert 8, wird die verknüpfte Liste in einen rot-schwarzen Baum umgewandelt
Rot-Schwarz-Baum-Algorithmus
Der Unterschied zwischen 1,8 und 1,7
ConcurrentLinkedQueue
Eine unbegrenzte threadsichere Warteschlange, die auf Verbindungsknoten basiert, das FIFO-Prinzip zum Sortieren von Elementen verwendet und intern mithilfe des CAS-Algorithmus implementiert wird
Unveränderlichkeit
Das nächste oder letzte Element in der Warteschlange ist null
Die Elemente aller nicht gelöschten Knoten in der Warteschlange dürfen nicht null sein und können vom Hauptknoten aus durchlaufen werden.
Damit der Knoten gelöscht werden soll, setzen Sie zunächst sein Elementfeld auf Null, anstatt ihn direkt auf Null zu setzen (der Iterator überspringt Knoten mit Nullelementen).
Lassen Sie zu, dass Head- und Tail-Updates verzögert werden. Was bedeutet das? Das bedeutet, dass Kopf und Schwanz nicht immer auf das erste und letzte Element zeigen (wird später erklärt).
Invarianz und Variabilität des Kopfes
Invarianz und Variabilität des Schwanzes
Subtilität: CAS wird verwendet, um Datenoperationen abzuschließen und gleichzeitig Inkonsistenzen in der Warteschlange zuzulassen, und eine schwache Konsistenz wird vollständig demonstriert.
ConcurrentSkipListMap
Die dritte Schlüsselwert-Datenstruktur: SkipList (Überspringliste)
SkipList
Ausgewogene binäre Baumstruktur
Mit der Sprungliste können die sortierten Daten in einer mehrschichtigen verknüpften Liste verteilt werden, wobei eine Zufallszahl von 0-1 verwendet wird, um zu bestimmen, ob Daten nach oben klettern oder nicht, und dabei ein Algorithmus zum „Austausch von Raum gegen Zeit“ verwendet wird. Jedem Knoten wird ein Vorwärtszeiger hinzugefügt, und einige Knoten, die nicht beteiligt werden können, können beim Einfügen, Löschen und Suchen ignoriert werden, wodurch die Effizienz verbessert wird.
charakteristisch
Es besteht aus vielen Strukturschichten, und die Ebenen werden mit einer bestimmten Wahrscheinlichkeit zufällig generiert.
Jede Ebene ist eine geordnete verknüpfte Liste. Sie kann je nach verwendetem Konstruktor auch nach dem beim Erstellen der Zuordnung bereitgestellten Komparator sortiert werden.
Die verknüpfte Liste der untersten Ebene (Ebene 1) enthält alle Elemente
Wenn ein Element in der verknüpften Liste der Ebene i erscheint, erscheint es auch in den verknüpften Listen unterhalb der Ebene i.
Jeder Knoten enthält zwei Zeiger, einer zeigt auf das nächste Element in derselben verknüpften Liste und einer auf das Element eine Ebene darunter.
Suchen, löschen, hinzufügen
ConcurrentSkipListSet
Intern implementiert mit ConcurrentSkipListMap
atomar
Grundtypklasse
Wird verwendet, um Grundtypen atomar zu aktualisieren
AtomicBoolean
Atomarer boolescher Aktualisierungstyp
AtomicInteger
Atomare Update-Ganzzahl
AtomicLong
Atomic Update lang
Array
Aktualisieren Sie ein Element in einem Array atomar
AtomicIntegerArray
Atomare Aktualisierung von Elementen in einem Integer-Array
AtomicLongArray
Atomare Aktualisierung von Elementen in einem Long-Integer-Array
AtomicReferenceArray
Atomare Aktualisierung von Elementen in einem Referenztyp-Array
Referenztyp
Wenn Sie mehrere Variablen atomar aktualisieren möchten, müssen Sie die von diesem atomaren Aktualisierungsreferenztyp bereitgestellte Klasse verwenden.
AtomicReference
Atomare Aktualisierung von Referenztypen
AtomicReferenceFieldUpdater
Atomare Aktualisierung von Feldern in Referenztypen
AtomicMarkableReference
Atomare Aktualisierung von Referenztypen mit Flag-Bits
Feldklasse
Wenn wir nur ein bestimmtes Feld in einer bestimmten Klasse benötigen, müssen wir die atomare Aktualisierung der Feldklasse verwenden
AtomicIntegerFieldUpdater
Updater zum atomaren Aktualisieren von Ganzzahlfeldern
AtomicLongFieldUpdater
Updater zum atomaren Aktualisieren langer Felder
AtomicStampedReference
Atomares Update des Referenztyps mit Versionsnummer
Blockierungswarteschlange
ArrayBlockingQueue
Eine FIFO-begrenzte Blockierungswarteschlange, implementiert als Array
ArrayBlockingQueue ist begrenzt und fest. Die Größe wird während des Konstruktors bestätigt. Änderungen werden nach der Bestätigung nicht unterstützt.
„Fairness“ ist in einer Multithread-Umgebung nicht garantiert
erreichen
ReentrantLock
Zustand
LinkedBlockingQueue
Linkbasierte, unbegrenzte FIFO-Blockierungswarteschlange
PriorityBlockingQueue
Unbegrenzte Blockierungswarteschlange mit Prioritätsunterstützung
Standardmäßig werden Elemente in aufsteigender Reihenfolge in natürlicher Reihenfolge sortiert. Sie können Elemente durch Angabe eines Komparators sortieren.
binärer Heap
Einstufung
Maximaler Haufen
Der Schlüsselwert des übergeordneten Knotens ist immer größer oder gleich dem Schlüsselwert eines beliebigen untergeordneten Knotens
Min. Haufen
Der Schlüsselwert des übergeordneten Knotens ist immer kleiner oder gleich dem Schlüsselwert eines beliebigen untergeordneten Knotens
Der Additionsvorgang „steigt ständig nach oben“, während der Löschvorgang ständig „abfällt“.
erreichen
ReentrantLock-Bedingung
binärer Heap
Verzögerungswarteschlange
Unbegrenzte Blockierungswarteschlange, die die verzögerte Erfassung von Elementen unterstützt
Anwendung
Cache: Löschen Sie die zwischengespeicherten Daten, die im Cache abgelaufen sind
Task-Timeout-Verarbeitung
erreichen
ReentrantLock-Bedingung
Prioritätswarteschlange sortiert nach Verzögerungszeit: PriorityQueue
Verzögerte Schnittstelle
Wird verwendet, um Objekte zu markieren, die nach einer bestimmten Verzögerungszeit ausgeführt werden sollen
Diese Schnittstelle erfordert, dass implementierende Klassen, die sie implementieren, eine CompareTo-Methode definieren müssen, die eine mit der getDelay-Methode dieser Schnittstelle konsistente Reihenfolge bereitstellt.
SynchronousQueue
Eine blockierende Warteschlange ohne Kapazität
Anwendung
Um Arbeit auszutauschen, werden der Thread des Produzenten und der Thread des Verbrauchers synchronisiert, um bestimmte Informationen, Ereignisse oder Aufgaben zu liefern
Schwer zu verstehen, hat Schwierigkeiten mit Exchanger
LinkedTransferQueue
Unbegrenzte Blockierungswarteschlange, bestehend aus einer verknüpften Liste
Entspricht einer Obermenge von ConcurrentLinkedQueue, SynchronousQueue (im fairen Modus), unbegrenzten LinkedBlockingQueues usw.
Präventivmodus
Wenn es verfügbar ist, nehmen Sie es direkt ein. Wenn nicht, wird es diese Position einnehmen, bis es erreicht wird oder eine Zeitüberschreitung oder Unterbrechung auftritt.
LinkedBlockingDeque
Eine bidirektionale Blockierungswarteschlange, die aus einer verknüpften Liste besteht
Die Kapazität ist optional. Sie können die Kapazität während der Initialisierung festlegen, um eine übermäßige Erweiterung zu verhindern. Wenn sie nicht festgelegt ist, ist die Standardkapazität Integer.MAX_VALUE.
verwenden
Muster „Job stehlen“.
Thread-Pool
Nutzen
Reduzieren Sie den Ressourcenverbrauch
Reduzieren Sie die Kosten für die Erstellung und Zerstörung von Threads, indem Sie erstellte Threads wiederverwenden
Verbessern Sie die Reaktionsgeschwindigkeit
Wenn eine Aufgabe eintrifft, kann die Aufgabe sofort ausgeführt werden, ohne auf die Erstellung des Threads warten zu müssen.
Verbessern Sie die Thread-Verwaltbarkeit
Einheitliche Zuordnung, Abstimmung und Überwachung
Testamentsvollstrecker
Testamentsvollstrecker
Die statische Factory-Klasse stellt statische Factory-Methoden der Klassen Executor, ExecutorService, ScheduledExecutorService, ThreadFactory, Callable und anderer bereit
ThreadPoolExecutor
Parameterbedeutung
corePoolSize
Die Anzahl der Kernthreads im Thread-Pool
maximalePoolSize
Die maximal zulässige Anzahl von Threads im Thread-Pool
keepAliveTime
Thread-Leerlaufzeit
Einheit
Einheit von keepAliveTime
Arbeitswarteschlange
Eine blockierende Warteschlange, in der Aufgaben gespeichert werden, die auf ihre Ausführung warten
Blockierungswarteschlange verwendet
ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue
PriorityBlockingQueue
ThreadFactory
Factory wird zum Einrichten der Thread-Erstellung verwendet
DefaultThreadFactory
Handler
RejectedExecutionHandler, Thread-Pool-Ablehnungsstrategie
Einstufung
AbortPolicy: Eine Ausnahme direkt auslösen, Standardrichtlinie
CallerRunsPolicy: Verwenden Sie den Thread, in dem sich der Aufrufer befindet, um Aufgaben auszuführen
DiscardOldestPolicy: Verwerfen Sie die vorderste Aufgabe in der Blockierungswarteschlange und führen Sie die aktuelle Aufgabe aus
DiscardPolicy: Die Aufgabe direkt verwerfen
Thread-Pool-Klassifizierung
newFixedThreadPool
Wiederverwendbarer Thread-Pool mit einer festen Anzahl von Threads
analysieren
corePoolSize stimmt mit maximumPoolSize überein
Verwendung einer „unbegrenzten“ Warteschlange LinkedBlockingQueue
maximalePoolSize, keepAliveTime, RejectedExecutionHandler ungültig
newCachedThreadPool
Executor, der einen einzelnen Arbeitsthread verwendet
analysieren
corePoolSize und maximumPoolSize sind auf 1 gesetzt
Verwenden Sie LinkedBlockingQueue als workerQueue
newSingleThreadExecutor
Ein Thread-Pool, der bei Bedarf neue Threads erstellt
analysieren
corePoolSize ist auf 0 gesetzt
MaximumPoolSize ist auf Integer.MAX_VALUE festgelegt
SynchronousQueue als WorkerQueue
Wenn der Hauptthread Aufgaben schneller sendet als die Threads in den MaximumPool-Prozessaufgaben, erstellt CachedThreadPool weiterhin neue Threads, was möglicherweise CPU- und Speicherressourcen erschöpft.
Aufgabenübermittlung
Executor.execute()
ExecutorService.submit()
Aufgabenausführung
Umsetzungsprozess
Optimierung des Thread-Pools
Zwei Modelle
Thread-Pool-Überwachung
ScheduledThreadPoolExecutor
Von ThreadPoolExecutor geerbt
Führen Sie die Aufgabe nach einer bestimmten Verzögerung aus oder führen Sie die Aufgabe regelmäßig aus
DelayQueue wird intern zur Implementierung verwendet und geplante Aufgaben werden in DelayQueue gestellt. DelayQueue kapselt intern PriorityQueue, das die ScheduledFutureTasks in der Warteschlange sortiert.
Zukunft
Asynchrone Berechnung
Zukunft
Operationen bereitstellen
Stornierung von Ausführungsaufgaben
Abfrage, ob die Aufgabe abgeschlossen ist
Erhalten Sie Ergebnisse der Aufgabenausführung
Zukunftsaufgabe
Implementieren Sie die RunnableFuture-Schnittstelle, die als Runnable ausgeführt oder als Future verwendet werden kann, um den Rückgabewert von Callable zu erhalten
Intern implementiert auf Basis von AQS