Heim  >  Artikel  >  Java  >  Lernen Sie Java-Parallelität: Sperrenoptimierung, ConcurrentHashMap, Sperrentrennung

Lernen Sie Java-Parallelität: Sperrenoptimierung, ConcurrentHashMap, Sperrentrennung

php是最好的语言
php是最好的语言Original
2018-07-30 10:02:151845Durchsuche

Einige Vorschläge zur Verbesserung der Sperrleistung


Reduzieren Sie die Sperrleistung Die Zeit

wird nur bei Bedarf synchronisiert, wodurch die Haltezeit der Sperre erheblich verkürzt, die Möglichkeit von Sperrkonflikten verringert und die Parallelitätsfähigkeiten verbessert werden

Verwenden Sie beispielsweise die Synchronisierungssperre „Synchronisieren“. Versuchen Sie, es hinzuzufügen, wenn das Objekt den Variablenstatus teilen muss. Anstatt blind vor der gesamten Methode eine Synchronisierung hinzuzufügen, sperren Sie das Objekt, das diese Methode aufruft, direkt, was die Wahrscheinlichkeit eines Sperrenwettbewerbs erhöht


Lesen- Schreibsperrentrennung ersetzt exklusive Sperre

Ich habe über die Verwendung der ReadWriteLock-Lese-/Schreibtrennung gesprochen, um die Effizienz zu verbessern

Eine Erweiterung ist die Sperrentrennungsstrategie

Um exklusive Sperren zu trennen Ein typisches Referenzszenario für diese Technologie ist die Aufgabenwarteschlange LinkedBlockingQueue. Wie bereits erwähnt, handelt es sich um eine unbegrenzte Aufgabenwarteschlange, die auf der Grundlage einer verknüpften Liste implementiert wird Die Warteschlange ist jeweils komplementär, sodass in der JDK-Implementierung zwei Sperren für diese beiden Operationen bereitgestellt werden.

Wenn Multithreads beispielsweise die put()-Operation ausführen, müssen sie dies am meisten tun konkurrieren um putLock, und die Take-Operation konkurriert um takeLock

 /** Lock held by take, poll, etc */
    private final ReentrantLock takeLock = new ReentrantLock();

    /** Wait queue for waiting takes */
    private final Condition notEmpty = takeLock.newCondition();

    /** Lock held by put, offer, etc */
    private final ReentrantLock putLock = new ReentrantLock();

    /** Wait queue for waiting puts */
    private final Condition notFull = putLock.newCondition();

Sperrenvergröberung

Sperren, die kontinuierlich dieselbe Ressource beantragt und freigegeben haben, können in eine einmalige Anfrage integriert werden für die Sperre, wodurch der Verbrauch von Anwendungs- und Freigabeaktionen reduziert wird.

Zum Beispiel:

for(int i=0;i<n;i++){
		synchronized(lock){}
		
	}

ist auf

 synchronized(lock){
        for(int i=0;i<n;i++){}
    }

optimiert, um die Sperrgranularität zu reduzieren

Ein typisches Anwendungsszenario dieser Technologie ist die Implementierung von ConcurrentHashMap. Im Vergleich zu HashMap ist es threadsicher und im Vergleich zu HashTable ist es eine effiziente Parallelität

ConcurrentHashMap-Implementierungsprinzip

Es gibt einen großen Unterschied zwischen der Implementierung von ConcurrentHashMap in jdk1.7 und jdk1.8

Die zugrunde liegende Struktur von ConcurrentHashMap ist ein Segment-Array. Die Standardgröße beträgt 16. Jedes Segment-Array kann als kleine HashMap betrachtet werden, was bedeutet, dass das Segment-Array mithilfe einer verknüpften Liste und implementiert wird ein Array.

  • Die Implementierung von JDK 1.7 basiert auf der Segmentierungssperre ConcurrentHashMap

Wenn wir beispielsweise ein neues Schlüssel-Wert-Paar in die Karte einfügen müssen, finden wir zunächst anhand des Hashcodes des Schlüssels heraus, in welches Segment es eingefügt werden soll, und dann Sperren Sie dieses Segment, um den Put-Vorgang abzuschließen, da die Segmentklasse von der ReentrantLock-Klasse erbt. Beim Erwerb der Sperre verwenden Sie die Sperre nicht direkt, um sie zu erhalten Die Methode bleibt hängen, wenn sie die Sperre nicht erhalten kann. Tatsächlich wird Spin Lock verwendet. Wenn tryLock die Sperre nicht erhalten kann, bedeutet dies, dass die Sperre zu diesem Zeitpunkt durch tryLock erneut angewendet wird. Beim Multithreading kann also echte Parallelität zwischen Threads erreicht werden, solange sich die eingefügten Daten nicht in einem Segment befinden und die Sperrkonkurrenz keine Blockierung verursacht.

Problem: Wenn segmentübergreifende Vorgänge erforderlich sind, d Sperren aller Segmente. Beispielsweise erfordert die size()-Methode von ConcurrentHashMap die Sperre aller Untersegmente

Lernen Sie Java-Parallelität: Sperrenoptimierung, ConcurrentHashMap, Sperrentrennung

  • Die Implementierung von jdk 1.8 basiert auf CAS ConcurrentHashMap

Die maximale Parallelität der ConcurrentHashMap von jdk1.7 entspricht der Anzahl der Segmente. Um die Parallelität weiter zu verbessern, hat jdk 1.8 die Segmentierungssperrlösung aufgegeben und direkt ein großes Array verwendet. Um gleichzeitig die Adressierungsleistung bei Hash-Kollisionen zu verbessern, konvertiert Java 8 die verknüpfte Liste (Adressierungszeitkomplexität ist O(N)) in einen Rot-Schwarz-Baum (Adressierungszeitkomplexität ist O(N)), wenn die Die Länge der verknüpften Liste überschreitet einen bestimmten Schwellenwert (8). Der Index des Schlüssels im Array wird auch bestimmt, indem das Modulo des Hash-Werts des Schlüssels und die Array-Länge verwendet werden.

Lernen Sie Java-Parallelität: Sperrenoptimierung, ConcurrentHashMap, Sperrentrennung

Für die Put-Operation, wenn das Array-Element entspricht Der Schlüssel ist null, dann wird er über eine CAS-Operation auf den aktuellen Wert gesetzt. Wenn das dem Schlüssel entsprechende Array-Element (d. h. der Kopf der verknüpften Liste oder das Stammelement des Baums) nicht null ist, verwenden Sie das synchronisierte Schlüsselwort, um eine Sperre für das Element zu beantragen, und führen Sie dann den Vorgang aus. Wenn die Put-Operation dazu führt, dass die Länge der aktuellen verknüpften Liste einen bestimmten Schwellenwert überschreitet, wird die verknüpfte Liste in einen Baum umgewandelt, wodurch die Adressierungseffizienz verbessert wird.

Da das Array bei Lesevorgängen mit dem Schlüsselwort volatile geändert wird, besteht kein Grund zur Sorge um die Sichtbarkeit des Arrays. Gleichzeitig ist jedes Element eine Node-Instanz (jedes Element in Java 7 ist ein HashEntry. Sein Schlüsselwert und sein Hash-Wert werden von final geändert und können nach der Änderung nicht geändert werden.) Sein Wert und der Verweis auf das nächste Element werden durch volatile geändert, und die Sichtbarkeit ist ebenfalls gewährleistet.

Größenoperation: Jedes große Array verwaltet einen Zähler. Sowohl die Put-Methode als auch die Remove-Methode verwalten die Größe der Map über die addCount-Methode. Die Size-Methode ermittelt über sumCount die Größe der von der addCount-Methode verwalteten Karte.

Es ist wichtig zu beachten, dass der Grund, warum jedes Array einen Zähler enthält, anstatt einen globalen Zähler in ConcurrentHashMap zu verwenden, darin besteht, die Parallelität von ConcurrentHashMap zu berücksichtigen: Weil dies der Fall ist Wenn Sie aktualisieren müssen Wenn Sie den Zähler verwenden, müssen Sie nicht die gesamte ConcurrentHashMap sperren.Es ist wichtig zu beachten, dass die Anzahl flüchtig ist, sodass alle Aktualisierungen der Anzahl sofort für andere Threads sichtbar sind.

Verwandte Artikel :

Java-Framework Bootstrap, jQuery, SpringMVC, Hibernate, hohe Leistung und hohe Parallelität

Lösung für Probleme mit hoher Parallelität im Java-System

Ähnliche Videos:

Java-Video-Tutorial

Das obige ist der detaillierte Inhalt vonLernen Sie Java-Parallelität: Sperrenoptimierung, ConcurrentHashMap, Sperrentrennung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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