Heim  >  Artikel  >  Java  >  ReadWriteLock-Schnittstelle und ihre Implementierung ReentrantReadWriteLock

ReadWriteLock-Schnittstelle und ihre Implementierung ReentrantReadWriteLock

巴扎黑
巴扎黑Original
2017-06-23 16:33:391212Durchsuche

Die Sperren im Sperrpaket des Java-Parallelitätspakets sind im Grunde der Schlüssel. Nachdem wir den Funktionsmechanismus des Synchronisierers AQS klar verstanden haben, wird es in diesem Kapitel tatsächlich einfacher sein konzentriert sich auf eine weitere wichtige Sperre – die Lese-/Schreibsperre ReentrantReadWriteLock.

ReentrantLock ist eine exklusive Sperre, was bedeutet, dass nur ein Thread die Sperre erwerben kann. Was aber, wenn das Szenario so aussieht, dass der Thread nur Lesevorgänge ausführt? Auf diese Weise ist ReentrantLock nicht sehr geeignet. Der Lesethread muss die Sicherheit seines Threads nicht gewährleisten. Nur so können Leistung und Effizienz bestmöglich gewährleistet werden. ReentrantReadWriteLock ist eine solche Sperre. Sie ist in eine Lesesperre und eine Schreibsperre unterteilt, aber nur ein Schreiboperationsthread kann die Schreibsperre erhalten Die Sperre ist eine gemeinsam genutzte Sperre (gemeinsamer Modus in AQS), und die Lesesperre ist eine exklusive Sperre (exklusiver Modus in AQS). Schauen wir uns zunächst die Schnittstellenklasse der Lese-/Schreibsperre an:

1 public interface ReadWriteLock {    
2     Lock readLock();        //获取读锁3     Lock writeLock();        //获取写锁4 }

Sie können sehen, dass die ReadWriteLock-Schnittstelle nur zwei Methoden definiert, die Methode zum Erwerb der Lesesperre und die Methode zum Erlangen der Schreibsperre. Das Folgende ist die Implementierungsklasse von ReadWriteLock – ReentrantReadWriteLock.

Ähnlich wie ReentrantLock implementiert ReentrantReadWriteLock auch den Synchronizer AQS über eine interne Klasse Sync. Es implementiert auch Sync, um faire Sperren und unfaire Sperren zu implementieren. Wie werden die in der ReadWriteLock-Schnittstelle erhaltenen Lese- und Schreibsperren implementiert?

//ReentrantReadWriteLockprivate final ReentrantReadWriteLock.ReadLock readerLock;private final ReentrantReadWriteLock.WriteLock writerLock;final Sync sync;public ReentrantReadWriteLock(){this(false);    //默认非公平锁}public ReentrantReadWriteLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();    //锁类型(公平/非公平)readerLock = new ReadLock(this);    //构造读锁writerLock = new WriteLock(this);    //构造写锁}
……public ReentrantReadWriteLock.WriteLock writeLock0{return writerLock;}public ReentrantReadWriteLock.ReadLock readLock0{return ReaderLock;}
//ReentrantReadWriteLock$ReadLockpublic static class ReadLock implements Lock {protected ReadLock(ReentrantReadwritLock lock) {
        sync = lock.sync;        //最后还是通过Sync内部类实现锁  }
    ……    //它实现的是Lock接口,其余的实现可以和ReentrantLock作对比,获取锁、释放锁等等}
//ReentrantReadWriteLock$WriteLockpublic static class WriteLock implemnts Lock {protected WriteLock(ReentrantReadWriteLock lock) {
        sync = lock.sync;
  }
……    //它实现的是Lock接口,其余的实现可以和ReentrantLock作对比,获取锁、释放锁等等}

Das Obige ist eine allgemeine Einführung in ReentrantReadWriteLock. Sie können dessen internes sehen. Es gibt mehrere Interne Klassen. Tatsächlich gibt es zwei Sperren in der Lese-/Schreibsperre. Diese beiden Sperren implementieren die Lock-Schnittstelle und können mit ReentrantLock verglichen werden durch Synchronizer AQS, der auch mit Sync in ReentrantLock verglichen werden kann.
Rückblickend auf AQS gibt es zwei wichtige Datenstrukturen: eine ist die Synchronisationswarteschlange und die andere ist der Synchronisationsstatus. Dieser Synchronisationsstatus wird auf die Lese-/Schreibsperre angewendet, bei der es sich um den Lese-/Schreibstatus handelt. In AQS gibt es jedoch nur einen Status. Die Ganzzahl stellt den Synchronisierungsstatus dar. In der Lese-/Schreibsperre müssen zwei Synchronisierungsstatus zum Lesen und Schreiben aufgezeichnet werden. Daher verarbeitet die Lese-/Schreibsperre die Status-Ganzzahl in AQS. Es handelt sich um eine int-Variable mit insgesamt 4 Bytes und 32 Bits. Dann können die Lese- und Schreibzustände jeweils 16 Bits belegen – die oberen 16 Bits repräsentieren das Lesen 16 Bit zeigen das Schreiben an.

 

Wenn der Wert von state 5 ist, lautet die Binärdatei (0000000000000000000000000101). Lesen und Schreiben? Dies erfordert den Einsatz von Verschiebungsoperationen. Die Berechnungsmethode lautet: Statusstatus & 0x0000FFFF schreiben, Statusstatus >>> lesen. Das Erhöhen des Schreibzustands um 1 entspricht Zustand + 1, und das Erhöhen des Lesezustands um 1 entspricht Zustand + (1 << 16). Informationen zum Schichtbetrieb finden Sie unter „<<, >>, >>>Schichtbetrieb“.

Erwerb und Freigabe von Schreibsperren

Basierend auf unseren bisherigen Erfahrungen können wir wissen, dass AQS das Algorithmusgerüst für den Erwerb von Sperren bereits eingerichtet hat und dies nur noch tun muss Die Implementierung erfolgt durch die Unterklasse tryAcquire (exklusive Sperre), daher müssen wir nur tryAcquire überprüfen.

 1 //ReentrantReadWriteLock$Sync 2 protected final boolean tryAcquire(int acquires) { 3     Thread current = Thread.currentThread; 4     int c = getState();    //获取state状态 5     int w = exclusiveCount(c);    //获取写状态,即 state & 0x00001111 6     if (c != 0) {    //存在同步状态(读或写),作下一步判断 7         if (w == 0 || current != getExclusiveOwnerThread())     //写状态为0,但同步状态不为0表示有读状态,此时获取锁失败,或者当前已经有其他写线程获取了锁此时也获取锁失败 8             return false; 9         if (w + exclusiveCount(acquire) > MAX_COUNT)    //锁重入是否超过限制10             throw new Error(“Maxium lock count exceeded”);11         setState(c + acquire);    //记录锁状态12         return true;13   }14   if (writerShouldBlock() || !compareAndSetState(c, c + acquires))15       return false;        //writerShouldBlock对于非公平锁总是返回false,对于公平锁则判断同步队列中是否有前驱节点16   setExclusiveOwnerThread(current);17   return true;18 }

Das Obige ist die Statuserfassung der Schreibsperre. Was schwer zu verstehen ist, ist die Methode, mit der unfaire Sperren direkt false zurückgeben fair locks it is Rufen Sie die hasQueuedPredecessors-Methode wie folgt auf:

1 //ReentrantReadWriteLock$FairSync2 final boolean writerShouldBlock() {3     return hasQueuedPredecessors();4 }

Was ist der Grund? Dies kommt auf den Unterschied zwischen unfairen Sperren und fairen Sperren zurück. Weitere Informationen finden Sie unter „5.Lock-Schnittstelle und ihre Implementierung ReentrantLock“. Bei unfairen Sperren erzwingt der Thread jedes Mal, wenn er eine Sperre erhält, unabhängig davon, ob sich Threads in der Synchronisationswarteschlange befinden. Wenn er den Thread nicht erhalten kann, wird er bis zum Ende der Warteschlange erstellt. für faire Sperren, solange die Synchronisationswarteschlange Threads in der Warteschlange enthält, wird die Sperre nicht erworben, sondern die Thread-Struktur wird am Ende der Warteschlange hinzugefügt. Zurück zum Erwerb des Schreibstatus: In der tryAcquire-Methode wurde festgestellt, dass kein Thread die Sperre hält, aber zu diesem Zeitpunkt werden entsprechende Vorgänge entsprechend den verschiedenen Sperren ausgeführt. Für unfaire Sperren - Sperren ergreifen, für faire Sperren - Synchronisationswarteschlange Es gibt Threads im Thread, keine Sperre, und sie werden am Ende der Warteschlange hinzugefügt.
Der Freigabevorgang der Schreibsperre ähnelt im Wesentlichen dem Freigabevorgang von ReentrantLock. Bei jeder Freigabe wird der Schreibstatus auf 0 reduziert, was bedeutet, dass die Schreibsperre vollständig aufgehoben wurde .

Erfassung und Freigabe von Lesesperren

Ebenso können wir aufgrund unserer bisherigen Erfahrung wissen, dass AQS bereits das Algorithmusgerüst für den Erwerb von Sperren eingerichtet hat implementiert tryAcquireShared (gemeinsame Sperre), daher müssen wir nur tryAcquireShared überprüfen. Wir wissen, dass Sperren im gemeinsam genutzten Modus von mehreren Threads gleichzeitig erworben werden können. Der T1-Thread erhält die Sperre und der Synchronisationsstatus ist zu diesem Zeitpunkt auch T2 Sperre, Status = 2, und dann der T1-Thread-Wiedereintrittsstatus = 3, was bedeutet, dass der Lesestatus die Summe aus der Anzahl der Lesesperren aller Threads und der Anzahl der Lesesperren jedes Threads ist In ThreadLock gespeichert und vom Thread selbst verwaltet, daher müssen hier einige Dinge erledigt werden. Komplexe Verarbeitung, der Quellcode ist etwas lang, aber die Komplexität liegt darin, dass jeder Thread die Anzahl der Lesesperren speichert Weitere Informationen finden Sie unter tryAcquireShared im Quellcode. Lesen Sie es sorgfältig durch und kombinieren Sie es mit der obigen Analyse der Schreibsperre.
Bemerkenswert an der Freigabe von Lesesperren ist die Anzahl der von ihr selbst aufrechterhaltenen Sperren und die Reduzierung des Statusstatus durch Schichtoperationen – (1 << 16).

Das obige ist der detaillierte Inhalt vonReadWriteLock-Schnittstelle und ihre Implementierung ReentrantReadWriteLock. 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