Der folgende Editor bringt Ihnen einen Artikel über die ReadWriteLock-Schnittstelle und ihre Implementierung der ReentrantReadWriteLock-Methode. Der Herausgeber findet es ziemlich gut, deshalb teile ich es jetzt mit Ihnen und gebe es als Referenz. Folgen wir dem Editor, um einen Blick darauf zu werfen
Die Sperren im Locks-Paket des Java-Parallelitätspakets sind im Wesentlichen der Schlüssel zum Verständnis des Funktionsmechanismus des Synchronizers AQS Es wird viel einfacher sein, diese Sperren zu analysieren. Dieses Kapitel konzentriert sich auf eine weitere wichtige Sperre: die Lese-/Schreibsperre ReentrantReadWriteLock.
ReentrantLock ist eine exklusive Sperre, was bedeutet, dass nur ein Thread die Sperre erhalten kann. Was aber, wenn das Szenario so aussieht, dass der Thread nur Lesevorgänge ausführt? Auf diese Weise ist ReentrantLock nicht sehr geeignet, die Sicherheit jedes Threads sicherzustellen. Nur so können Leistung und Effizienz gewährleistet werden möglich. 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:
public interface ReadWriteLock { Lock readLock(); //获取读锁 Lock writeLock(); //获取写锁 }
Sie sehen, dass die ReadWriteLock-Schnittstelle nur zwei Methoden definiert, die Methode zum Erlangen der Lesesperre und die Methode zum Erlangen des Schreibens sperren. Das Folgende ist die Implementierungsklasse von ReadWriteLock – ReentrantReadWriteLock.
Ähnlich wie ReentrantLock implementiert ReentrantReadWriteLock auch den Synchronisierer 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?
//ReentrantReadWriteLock private 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$ReadLock public static class ReadLock implements Lock { protected ReadLock(ReentrantReadwritLock lock) { sync = lock.sync; //最后还是通过Sync内部类实现锁 } …… //它实现的是Lock接口,其余的实现可以和ReentrantLock作对比,获取锁、释放锁等等 }
//ReentrantReadWriteLock$WriteLock public 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 sehen, dass darin tatsächlich mehrere interne Klassen enthalten sind Es gibt zwei Sperren in der Schreibsperre – ReadLock und WriteLock. Diese beiden Sperren implementieren die Self-Lock-Schnittstelle und können mit ReentrantLock verglichen werden. Die interne Implementierung dieser beiden Sperren ist der Synchronisierer AQS verglichen mit Sync in ReentrantLock.
Rückblickend auf AQS gibt es zwei wichtige Datenstrukturen: eine ist die Synchronisierungs--Warteschlange und die andere ist der Synchronisierungs--Status . Dieser Synchronisierungsstatus wird auf die angewendet Das heißt, der Lese- und Schreibstatus, aber in AQS gibt es nur eine Status-Ganzzahl, die den Synchronisationsstatus darstellt. Bei der Lese-/Schreibsperre müssen zwei Synchronisationsstatus 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 077857ee6de62c21adb0325ef2b1893a>, >>> Verschiebungsoperation “.
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.
//ReentrantReadWriteLock$Sync protected final boolean tryAcquire(int acquires) { Thread current = Thread.currentThread; int c = getState(); //获取state状态 int w = exclusiveCount(c); //获取写状态,即 state & 0x00001111 if (c != 0) { //存在同步状态(读或写),作下一步判断 if (w == 0 || current != getExclusiveOwnerThread()) //写状态为0,但同步状态不为0表示有读状态,此时获取锁失败,或者当前已经有其他写线程获取了锁此时也获取锁失败 return false; if (w + exclusiveCount(acquire) > MAX_COUNT) //锁重入是否超过限制 throw new Error(“Maxium lock count exceeded”); setState(c + acquire); //记录锁状态 return true; } if (writerShouldBlock() || !compareAndSetState(c, c + acquires)) return false; //writerShouldBlock对于非公平锁总是返回false,对于公平锁则判断同步队列中是否有前驱节点 setExclusiveOwnerThread(current); return true; }
Das Obige ist die Statuserfassung der WriterShouldBlock-Methode. Diese Methode wird oben direkt beschrieben, während für faire Sperren die hasQueuedPredecessors-Methode verwendet wird heißt wie folgt:
//ReentrantReadWriteLock$FairSync final boolean writerShouldBlock() { return hasQueuedPredecessors(); }
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 wird jedes Mal, wenn ein Thread eine Sperre erwirbt, zunächst der Sperrenerwerbsvorgang erzwungen, unabhängig davon, ob sich Threads in der Synchronisationswarteschlange befinden. Wenn er den Thread nicht erhalten kann, wird er am Ende der Warteschlange aufgebaut. 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 Freigabeprozess der Schreibsperre ähnelt im Wesentlichen dem Freigabeprozess von ReentrantLock. Bei jeder Freigabe wird der Schreibstatus auf 0 reduziert, was bedeutet, dass die Schreibsperre aktiviert ist vollständig freigegeben worden.
Erwerb und Freigabe der Lesesperre
In ähnlicher Weise können wir aufgrund unserer bisherigen Erfahrung wissen, dass AQS bereits das Algorithmusgerüst für den Erwerb von Sperren eingerichtet hat. Nur Unterklassen müssen tryAcquireShared (gemeinsame Sperre) implementieren, sodass wir nur tryAcquireShared überprüfen müssen. 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 der Lesesperre ist die Häufigkeit, mit der sie die Sperrerfassung aufrechterhält, und die Reduzierung des Statusstatus durch den Schiebevorgang – (1 << 16).
Das obige ist der detaillierte Inhalt vonDie ReadWriteLock-Schnittstelle von JAVA und ihre Implementierung der ReentrantReadWriteLock-Methode. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!