あなたは本当にまだReentrantLockのを理解していますか?

実行ホスティングJVMに同期され、ロックロックは、コードによって達成されます。そのため、より柔軟なロックと、開発者は、適切なシーンに合わせて操作しやすいように、ロックはインターフェースが使用するためにそれを実装する必要があり、ReetrantLockメイン実装クラスのロックのは、ReetrantLockはリエントラントロックされ、かつ公正なロックを指定することができますし、不公平ロック、我々は彼の特定の実装を見ています。

、ReentrantLockの使用

        ReentrantLock lock = new ReentrantLock();
		//如果被其它线程占用锁,会阻塞在此等待锁释放
		lock.lock();
		try {
			//操作
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			//释放锁
			lock.unlock();  
		}
复制代码

上記だけ一実施形態は、ロックによってロック()メソッドは、ロック解除を使用して()メソッドは、ロック動作を解除する、ReentrantLockのオブジェクトを作成し、比較的簡単なReentrantLockのを使用するように見ることができるされています。

彼はロック、のtryLock、のtryLock(ロング、TimeUnitで)指定された時間パラメータを使用して、3通りの方法でロックされています。ロックを取得するには、ロックを使用して、ロックが別のスレッドによって保持されている場合、それは待機状態になります。また、私たちは、例外が発生した場合でも、ロックを解除するロック解除メソッドを呼び出すためのイニシアチブを取る必要があり、彼はロックを解除するためのイニシアチブを取ることはありません、我々は明示的に解放する必要があります。メソッドを使用してロックを取得するのtryLock、戻り値があり、成功すればリターンが真の取得、虚偽のリターンを得るために失敗は待機状態ではなかったでしょう。(長い、TimeUnitで)パラメータがロックを取得するための時間を指定するのtryLock使用し、ロックはtrueを返し、falseを返します待機時間内にタイムアウトを取得しています。また、スレッドがロックを取得するために待機している場合、スレッドの状態を待って中断することができ、ロックを解除するlockInterruptiblyの道を呼び出すことができます。

第二に、リエントラントロックは何ですか

我々は上記の言っReentrantLockのは、再入可能ロックされ、その後、何がリエントラント、それをロックしていますか?リエントラントロック手段は、スレッドロックが同じロック上で繰り返されてもよいが、デッドロックを回避することができるように、ブロックされていません。のは、検証コードの再入可能ロックを見てみましょう:

public static class TestReentrantLock {
	private Lock lock = new ReentrantLock();
	public void method() {
		lock.lock();
		try {
			System.out.println("方法1获得ReentrantLock锁");
			method2();
		} finally {
			lock.unlock();
		}
	}
	public void method2() {
		lock.lock();
		try {
			System.out.println("方法2重入ReentrantLock锁");
		} finally {
			lock.unlock();
		}
	}
	public static void main(String[] args) {
			new TestReentrantLock().method();
	}
}
复制代码

私たちは、上記のコードによって、ReentrantLockのは、再入を持っていることがわかります。だから、実装の基礎となるこのリエントラント、それは何ですか?

リエントラントは、基本となる実装をロック

ReentrantLockのAQSた状態で使用される値であり、スレッドはロック状態の値を増加させるために保つことができ、対応する必要がゼロ状態になるまでロックを解除するロックを解除します。そして状態の値が揮発性で修飾されている、以下のソースコードの実装固有です。

 //java.util.concurrent.locks.ReentrantLock.FairSync
 protected final boolean tryAcquire(int acquires) {
 	//获取当前线程
    final Thread current = Thread.currentThread();
    int c = getState();
    //当前锁没被占用
    if (c == 0) {
        //1.判断同步队列中是否有节点在等待
 	   if (!hasQueuedPredecessors() &&
 		   compareAndSetState(0, acquires)) {//2.如果上面!1成立,修改state值(表明当前锁已被占用)
            //3.如果2成立,修改当前占用锁的线程为当前线程
 		   setExclusiveOwnerThread(current);
 		   return true;
 	   }
    }
    //占用锁线程==当前线程(重入)
    else if (current == getExclusiveOwnerThread()) {
 	   int nextc = c + acquires;//
 	   if (nextc < 0)
 		   throw new Error("Maximum lock count exceeded");
        //修改status
 	   setState(nextc);
 	   return true;
    }
    //直接获取锁失败
    return false;
}

复制代码

第三に、公正ロックするものです

私たちは、その後、何が公正なロックで、ReentrantLockのは、公正かつ不公平ロックロックを達成することができ、以前言いましたか?非ロック、それを公正とは何ですか?いわゆるフェアロックは、つまり、複数のスレッドがために、スレッドの要求を確実にするために、同じリソースを取得するために要求すると、実装に続いて、公正な競争の結果を確認します。逆に、不当なロックはイエス誰をつかんで誰が、各スレッドが競争するために一緒にロックし、注文要求スレッドに従っていません。

私たちは、公正なReentrantLockのロックを継承AQSを通じて達成される上に、我々は彼の特定の実装を見て言いました。

AQS

AQSは、主に相続の方法によって使用される抽象クラスです。AQS機能は、二つのタイプに分けられます。共有と排他。AQS実装がFIFOキュー双方向である内部同期キューに依存している現在のスレッドのロック競合が失敗した場合、現在のスレッドとAQSは、状態情報がキューに追加されたノードを同期するように構成されて待機し、その後、スレッドをブロックしました。スレッドがロック取得のロックを解除すると、それはキューからブロックされたノード(スレッド)を起動します。状態> 0が発現した場合、ロックが解除された状態= 0、ロックが取得された場合を示している場合int型同期状態変数の状態を使用してAQS部材が、示されています。

ロック競合がある場合、新しいスレッドが参加する場合、このスレッドは、同期キューにカプセル化されたノードのノードと新しいスレッドのノードへの前方ポインタは、ノード上の後方ポインタに追加されます新しいノードのスレッドを指します。これは、CASへのポインタを変更することによって行われます。

ヘッドノードロックが解除されたときに、それが成功した後続ノードがロックを取得する場合は、次のノードを覚ます最初のノードに自分自身を置くには、ヘッドノードは、設定がロック・スレッドを取得するためにヘッドノードによって行われるため、CASで設定する必要はありません。 、および同期ロックはありませんので保証CASスレッドによって得ることができます。

フェアロックソース

ロックを待っている、(同期が直接ロックを取得する際に、キューが空である)、同期キューを追加しました。

//java.util.concurrent.locks.ReentrantLock.FairSync
final void lock() {
	acquire(1);
}
//java.util.concurrent.locks.AbstractQueuedSynchronizer
public final void acquire(int arg) {
	if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
		selfInterrupt();
}

复制代码

tryAcquire():テンプレートメソッド取得ロック

//java.util.concurrent.locks.AbstractQueuedSynchronizer
//1
private Node addWaiter(Node mode) {
 //生成node
    Node node = new Node(Thread.currentThread(), mode);
    Node pred = tail;
    if (pred != null) {
 	//将node加到队列尾部
 	   node.prev = pred;
 	   if (compareAndSetTail(pred, node)) {
 		   pred.next = node;
 		   return node;
 	   }
    }
    //如果加入失败(多线程竞争或者tail指针为null)
    enq(node);
    return node;
}
//1.1  
private Node enq(final Node node) {
 //死循环加入节点(cas会失败)
    for (;;) {
 	   Node t = tail;
 	   if (t == null) { //tail为null,同步队列初始化
 		//设置head指针
 		   if (compareAndSetHead(new Node()))//注意这里是个空节点!!
 			   tail = head;//将tail也指向head
 	   } else {
 		   node.prev = t;//将当前node加到队尾
 		   if (compareAndSetTail(t, node)) {
 			   t.next = node;
 			   return t;//注意这里才返回
 		   }
 	   }
    }
}
//2
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
 	//表示是否被打断
 	   boolean interrupted = false;
 	   for (;;) {
 		//获取node.pre节点
 		   final Node p = node.predecessor();
 		   if (p == head //当前节点是否是同步队列中的第二个节点
 		   && tryAcquire(arg)) {//获取锁,head指向当前节点
 			   setHead(node);//head=head.next
 			   p.next = null;//置空 
 			   failed = false;
 			   return interrupted;
 		   }

 		   if (shouldParkAfterFailedAcquire(p, node) && //是否空转(因为空转唤醒是个耗时操作,进入空转前判断pre节点状态.如果pre节点即将释放锁,则不进入空转)
 			   parkAndCheckInterrupt())//利用unsafe.park()进行空转(阻塞)
 			   interrupted = true;//如果Thread.interrupt()被调用,(不会真的被打断,会继续循环空转直到获取到锁)
 	   }
    } finally {
 	   if (failed)//tryAcquire()过程出现异常导致获取锁失败,则移除当前节点
 		   cancelAcquire(node);
    }
}

复制代码

acquireQueued(addWaiter(Node.EXCLUSIVE)、引数):同期キューを追加します。

//java.util.concurrent.locks.AbstractQueuedSynchronizer
//1
private Node addWaiter(Node mode) {
 //生成node
    Node node = new Node(Thread.currentThread(), mode);
    Node pred = tail;
    if (pred != null) {
 	//将node加到队列尾部
 	   node.prev = pred;
 	   if (compareAndSetTail(pred, node)) {
 		   pred.next = node;
 		   return node;
 	   }
    }
    //如果加入失败(多线程竞争或者tail指针为null)
    enq(node);
    return node;
}
//1.1  
private Node enq(final Node node) {
 //死循环加入节点(cas会失败)
    for (;;) {
 	   Node t = tail;
 	   if (t == null) { //tail为null,同步队列初始化
 		//设置head指针
 		   if (compareAndSetHead(new Node()))//注意这里是个空节点!!
 			   tail = head;//将tail也指向head
 	   } else {
 		   node.prev = t;//将当前node加到队尾
 		   if (compareAndSetTail(t, node)) {
 			   t.next = node;
 			   return t;//注意这里才返回
 		   }
 	   }
    }
}
//2
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
 	//表示是否被打断
 	   boolean interrupted = false;
 	   for (;;) {
 		//获取node.pre节点
 		   final Node p = node.predecessor();
 		   if (p == head //当前节点是否是同步队列中的第二个节点
 		   && tryAcquire(arg)) {//获取锁,head指向当前节点
 			   setHead(node);//head=head.next
 			   p.next = null;//置空 
 			   failed = false;
 			   return interrupted;
 		   }

 		   if (shouldParkAfterFailedAcquire(p, node) && //是否空转(因为空转唤醒是个耗时操作,进入空转前判断pre节点状态.如果pre节点即将释放锁,则不进入空转)
 			   parkAndCheckInterrupt())//利用unsafe.park()进行空转(阻塞)
 			   interrupted = true;//如果Thread.interrupt()被调用,(不会真的被打断,会继续循环空转直到获取到锁)
 	   }
    } finally {
 	   if (failed)//tryAcquire()过程出现异常导致获取锁失败,则移除当前节点
 		   cancelAcquire(node);
    }
}

复制代码

selfInterrupt():現在のスレッドウェイクアップ

static void selfInterrupt() {//在获取锁之后 响应intterpt()请求
	Thread.currentThread().interrupt();
}

复制代码

これらは、私たちが収穫を持つことができReadWriteLock原理と希望のソースを解釈しています。

パートリファレンス:juejin.im/post/5ae1b4 ...

おすすめ

転載: juejin.im/post/5d50cdbdf265da03f333453a