リエントラントロック、ソースコードのロック/ロック解除の解釈



Reentrant Lock Interpretation Lock Unlock Source Code



再入可能ロックは、現在のスレッドによってロックされているオブジェクトを指し、このスレッドに再度アクセスできることがわかっています。 ReentrantLockとsynchronizedは、どちらも再入可能ロックです。
ダグ・リーが書いたメモを見てください。
*

A {@code ReentrantLock} is owned by the thread last * successfully locking, but not yet unlocking it. A thread invoking * {@code lock} will return, successfully acquiring the lock, when * the lock is not owned by another thread. The method will return * immediately if the current thread already owns the lock. This can * be checked using methods {@link #isHeldByCurrentThread}, and {@link * #getHoldCount}.

ReentrantLockの所有者は、最後のロックのスレッドです

*

It is recommended practice to always immediately * follow a call to {@code lock} with a {@code try} block, most * typically in a before/after construction such as: *最後のコードブロックでlock()とunlock()の後にtryコードブロックを使用することをお勧めします


1.ReentrantLockの全体的な構造
まず、ReentrantLockクラスの全体的な構成を確認します。

2つのプライベート変数:serialVersionUID言うまでもなく、シリアル化、同期はタイプSyncの変数です
Syncは内部抽象クラスであり、これから継承された2つの内部クラスがあります。メモに記述されているReentrantLockに対応するFairSyncとNotfairSyncは、フェアロックとアンフェアロック機能を実現できます。詳細については、後で説明します。
次に、コンストラクター、追加とロック解除、その他のメソッドとオブジェクトプロパティなどがあり、いくつかの重要な実装メソッドについて説明します。

2.ReentrantLockの構築方法とフェア/アンフェアロックの実装方法
パラメータコンストラクタはありません。デフォルトでは、不公平な再入可能ロックインスタンスが作成されます。
/** * Creates an instance of {@code ReentrantLock}. * This is equivalent to using {@code ReentrantLock(false)}. */ public ReentrantLock() { sync = new NonfairSync() }パラメーターコンストラクター付き(ブール値)
public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync() }オブジェクトが作成されると、syncに値が割り当てられ、ロック状態を取得するための後続のロックとロック解除はsyncによって管理されます。
SYNCクラス図では、キューがAQSを介して実装されていることがわかります。


3.ロックの実装
最初にメソッドノートを見て、ロックプロセスを理解します。
/** * Acquires the lock. * *

Acquires the lock if it is not held by another thread and returns * immediately, setting the lock hold count to one. * *

If the current thread already holds the lock then the hold * count is incremented by one and the method returns immediately. * *

If the lock is held by another thread then the * current thread becomes disabled for thread scheduling * purposes and lies dormant until the lock has been acquired, * at which time the lock hold count is set to one. * If the lock is not held by another thread, then return immediately and set the holding number = 1 * If the current thread already holds the lock, then return immediately, and the number of holding +1 * If it is held by another thread, enter the waiting queue */ public void lock() { sync.lock() }(1)非現実的なロックロックの実装

/** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()) else acquire(1) }不公平なロックの核となる考えはまだ残っていることがわかります 場合 、stateOffsetを0から1に更新します。ここでのstateOffsetは、実際にはAbstractQueuedSynchronizerクラスの状態プライベート変数です(キューはAQSによって制御されていると言われています)
/** * Atomically sets synchronization state to the given updated * value if the current state value equals the expected value. * This operation has memory semantics of a {@code volatile} read * and write. * * @param expect the expected value * @param update the new value * @return {@code true} if successful. False return indicates that the actual * value was not equal to the expected value. */ protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update) }

CASが合格し、現在のロックがどのスレッドによっても占有されていないことを示している場合、



AbstractOwnableSynchronizer.exclusiveOwnerThread = Thread.currentThread()



取得のソースコードを見てください(1):
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt() }不公平なロックでのtryAcquireの実装は、主に、ロック(CAS)を占有しているスレッドがあるかどうかを再度判断することです。現在のスレッドがすでにロックを保持している場合は、state + 1を実行し、すぐにtrueを返します。
/** * Performs non-fair tryLock. tryAcquire is implemented in * subclasses, but both need nonfair try for trylock method. */ final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread() //Check again if the state is equal to 0, that is, whether there is a thread occupying the lock int c = getState() if (c == 0) { //Same as CAS in the previous step if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current) return true } } //The lock is occupied by the current thread else if (current == getExclusiveOwnerThread()) { //state+1, update state int nextc = c + acquires if (nextc <0) // overflow throw new Error('Maximum lock count exceeded') setState(nextc) return true } return false } tryAcquire(1)がfalseを返す場合、つまり他のスレッドがすでに現在のロックを保持している場合は、次のステップに進みます。acquireQueued(addWaiter(Node.EXCLUSIVE)、arg)このステップは実際にはAQSの実装です。


addWaiter(Node.EXCLUSIVE)を最初に見てください
private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode) // Try the fast path of enq backup to full enq on failure Node pred = tail //If the tail node is not empty, update the tail node to the inserted data, and update prev to point to the original tail node if (pred != null) { node.prev = pred if (compareAndSetTail(pred, node)) { pred.next = node return node } } //If the original tail is empty or the CAS update failed in the previous step, the insert node is executed here enq(node) return node }キューは従来のリンクリストの実装であるため、特定のアルゴリズムの実装は繰り返されないことがわかります。待機するスレッドは、これまでのヘッドポインタとテールポインタを含むノードの形式でオブジェクトとしてカプセル化され、キューの構築が完了します。

次に、acquireQueuedメソッドを見てください

/** * Acquires in exclusive uninterruptible mode for thread already in * queue. Used by condition wait methods as well as acquire. * * @param node the node * @param arg the acquire argument * @return {@code true} if interrupted while waiting */ //The incoming is the Node node added by addWaiter in the previous step final boolean acquireQueued(final Node node, int arg) { boolean failed = true try { boolean interrupted = false for () { //Get the prev node passed into node final Node p = node.predecessor() //If P is the head node, that is, the first one in the waiting queue, execute the tryAcquire method again, that is, return true if the request node succeeds (see the code above for specific processing) if (p == head && tryAcquire(arg)) { //node position moves forward setHead(node) p.next = null // help GC failed = false return interrupted } //Whether to hang if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true } } finally { if (failed) cancelAcquire(node) } }この時点で、不公平ロックのロックメソッドが解析されます。公平ロックとの最大の違いは、tryAcquireメソッドに、現在のスレッドがヘッドであるかどうかを検出する追加のステップがあることです。先入れ先出し:
/** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */ protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread() int c = getState() if (c == 0) { //Check if the current thread is the first in the queue if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current) return true } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires if (nextc <0) throw new Error('Maximum lock count exceeded') setState(nextc) return true } return false } } 4.ロック解除の実装
ロックと比較して、ReentrantLockのロック解除の実装は少し簡単です。最初にソースコードに移動します。
/** * Attempts to release this lock. * *

If the current thread is the holder of this lock then the hold * count is decremented. If the hold count is now zero then the lock * is released. If the current thread is not the holder of this * lock then {@link IllegalMonitorStateException} is thrown. * * @throws IllegalMonitorStateException if the current thread does not * hold this lock */ public void unlock() { sync.release(1) }メモは単純明快です。ロックを解除すると、現在保持されているロックの数が減少します。ホールド数= 0の場合、現在のスレッドがロック保持スレッドでない場合はロックが解除され、例外がスローされます。
以下の特定の実装方法を見てみましょう。
/** * Releases in exclusive mode. Implemented by unblocking one or * more threads if {@link #tryRelease} returns true. * This method can be used to implement method {@link Lock#unlock}. * * @param arg the release argument. This value is conveyed to * {@link #tryRelease} but is otherwise uninterpreted and * can represent anything you like. * @return the value returned from {@link #tryRelease} */ public final boolean release(int arg) { if (tryRelease(arg)) {//Release the lock, state-1, return true when it is 0 Node h = head if (h != null && h.waitStatus != 0) //The head node is not empty and is not in the waiting state unparkSuccessor(h)//Call the Unsafe.unpack method to make the current thread available, this thread called the parkAndCheckInterrupt() method for pack processing during lock return true } return false }
protected final boolean tryRelease(int releases) { int c = getState()-releases //current state-1 if (Thread.currentThread() != getExclusiveOwnerThread()) //Whether it is the current thread throw new IllegalMonitorStateException() boolean free = false if (c == 0) {//until state==0 free = true //flag, equal to true means the current lock is idle setExclusiveOwnerThread(null) } setState(c)//Update state return free }

これで、ReentrantLockのロックとロック解除のソースコードの分析は完了しました。要約すると、ReentrantLockのロックは実際にはUnsafe.packによって一時停止され、ロック解除はUnsafe.unpack要求によって実行され、ロックを取得するために管理する複数のスレッドはCLHキューを通過します。成し遂げる。



将来時間があれば、他のメソッドのソースコードの解釈を更新してください。