記事が書かれており、不公平公正なロックがロックされている前に、次のステップは、彼が共有ロックと排他ロックは、定義により、排他的な、唯一のスレッドによって保持することが可能である、第2のロックを導入しました。そして、共有、それが複数のスレッドで共有することができます。
ロックカテゴリー
1.ロック公正/不公平ロック
2. 3.リエントラント5.楽観的ロック/悲観的ロックセグメント6ロック7バイアスロックをロック/ライト排他ロック/共有ロック4.ミューテックスをロック/軽量/ヘビーロック8をロックします。スピンロック
私たちがロックされ、公正かつ不当なロックと述べている前に、共有するために初めて、私たちはグループを見て、この時間は、あなたは、この排他ロックと共有ロックを解決します。
排他ロック
多くの排他ロックの名がある実際には、それは排他ロックとも呼ばれ、排他ロックと呼ばれていた、実際には、一般的に意味があり、
排他ロック、1つのスレッドのみ保持することができ、
私たちは、公正かつ不公平なロックのロックの前に、彼の例はまた、我々は、この例では見ることができ、一度それを言いました、
ReentrantLockの(排他)
ReentrantLockのを達成するためにAQSをベースに、AQSは、それは何ですか?
AQSは立っAbstractQueuedSynchronizer、「サマリラインシンクロナイザ」ビューの翻訳ソフトポイントを使用したが、多くの人が抽象キュー同期それを呼びたい場合。実際にはそれほど重要なことはありませんでした何を、単に英語を覚えて、これが最も重要です。
AQSそれは、共有リソースへのマルチスレッドフレームシンクロナイザアクセスのセットを定義し、多くのクラスは、例えばReentrantLockのAQSに依存している、私たちは紹介したいと思います。
あなたは、ソースコードを参照してください
/*
查询是否有任何线程正在等待与此锁相关联的给定条件。
请注意,由于超时和*中断可能随时发生,
此方法主要用于监视系统状态
*/
public boolean hasWaiters(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}
ここでは、我々はReentrantLockの依存AQSで話をしている、とAQSは、それが中JUCのコアと契約の構成要素であることを示します。それは必須成分です。
AQSサブああは、同期状態、FIFO同期待ち行列を取得例えば問題、同期が向けられている多数の詳細を、解決します。シンクロナイザーを構築するには、AQSベースの多くの利点をもたらすことができます。これは、大幅に実装の手間を減らすことができますし、複数の場所で発生する競争上の問題に対処する必要はありませんではないだけ。
AQSはブロッキングはそれによってコンテキストスイッチのオーバーヘッドを低減する、唯一の時間で起こる、ベースの同期を構築し、スループットが向上します。
主な用途は、サブクラスは、シンクロナイザを継承し、同期状態を管理するための抽象メソッドによって実装され、AQSの継承です。
あなたは見ることができます
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
private static final long serialVersionUID = 7373984972572414691L;
そしてReentrantLockの中にその典型的な例:
ロック状態> 0を取得した場合を示し、同期状態を表すためにint型部材の状態変数を使用して
これは、我々はint型、C = getStateを()の前に見たものです。
Cは、スレッドが所有しているときにロックが現在記載されていない0に等しい三つの方法提供する場合(getStateを()、SETSTATEを(INT NewStateに)、compareAndSetState(INT)、INTの更新を期待する)状態同期状態を操作するために、AQSので状態の動作を保証することは安全です。
AQSについて私はそんなには慎重に検討するために説明するだろう何を理解したい、そしてこのReentrantLockのソースコードは、ある場合には
/**
它默认是非公平锁
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
创建ReentrantLock,公平锁or非公平锁
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
/**
而他会分别调用lock方法和unlock方法来释放锁
*/
public void lock() {
sync.lock();
}
public void unlock() {
sync.release(1);
}
しかし、実際には、彼はちょうどまた、ロックを呼び出し、メソッドのロックを解除し、我々は少し問題を通すことができないためではなく、この時点では、待ち状態に入った場合には何のunparkを()メソッドが存在しない場合は、彼をウェイクする方法はありませんので、あるシーンのニーズを満たすために、ロックやスーパーマーケットしようとする何かをするのtryLock()、のtryLock(長い、TimeUnitでの)登場かかとに来ます。
ReentrantLockのメソッドボディは、唯一のスレッドが同時に、このコードを実行する、または同時に一つだけスレッドロック方法が返すことことを保証します。残りのスレッドがロックを取得するまで中断されます。
ロックを取得する唯一のスレッドがあり、スレッドのすべての残りの部分がロックを解除にロックを保持しているスレッドまで中断され、中断されたスレッドがウェイクアップ:ここからは、実際には、排他ロック機能ReentrantLockのが実現見ることができますロック競合を再起動します。
取得メソッドを呼び出すことにより、排他ロックを取得するためのソースコードでは、この方法は、実際には、AQSによってブロックされます
/**
*以独占模式获取,忽略中断。通过至少调用tryAcquire实现
成功返回。否则线程排队,可能重复阻塞和解除阻塞,
调用tryAcquire直到成功。
此方法可用于实现方法lock。
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
これはtryAcquireを介してロックを取得しようとする(サブクラス同期によって実現される)、このロックの方法のステップを実施する上記の供給源であります
そして、AQSの呼び出しacquireQueued方法をロックするために得ることはありません。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
これは、一般的に電流駆動ノードがヘッドノードであることを意味し、そしてのみ排他的に返すことを意味します
場合は、次の判断ならば、彼がブロックされて行くだろうが、また、中断するか否かを判断するために、私たちのノードの状態が完成Node.SIGNAL、ある場合、スレッドはスレッドのリリースがあることを知ってparkAndCheckInterruptメソッドを実行しますこの時間は、ロックを取得するためのサイクルにunparkをされます。そして、この方法のLockSupport.park(これは)現在のスレッドWATINGの状態を中断し、私たちは、私は、このようなFIFO機構で待機している、そのリリースを言ったことを、彼をウェイクする方法パークを解除実行する必要がありLOCK操作が実現されています。
この上記のコードは、単にロックすることが、私たちは解放されないロックを取得する場合、それはデッドロック状況に自然であるので、ロックを解除しませんでした!
リリースを行う必要があります、
私たちは、内部を見てロックを解除する方法です
public void unlock() { sync.release(1); }
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
リリースを完了するために、間接呼び出しAQSリリース(1)の方法のロックを解除
tryRelease(int)メソッドは、そうでなければ偽である、ヘッドが着信unparkSuccessor(ノード)メソッドを設定する場合は、特別な決意であり、真を返します。
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
そして彼はunparkSuccessorメソッドを実行し、それがすでに本当にロックを解除することを意味します。これは実際に排他ロックロックを取得する処理で、ロックを解除します!利害関係者は、それを翻訳したソースコードのコメントに移動して見ることができます。
共有ロック
我々は排他的に私たちの前の排他ロックは共有ロックが実際にタグをロックするために同様の状態で使用されていることがわかりますが、誰もJAVAは2つの状態がありたくないので、そこの違い、彼らときロック状態と同じではありません。
基本的なプロセスは同じであり、主な違いは、同期ステータスが非ゼロである場合、それはロックを共有しているので、ロック獲得判定条件は、それがその後、同時に同期状態の数が1より大きい場合、複数のスレッドが同時に取得できることです同期ステータスが共有ロック数がすべて取得されている唯一のこと、0である場合にのみ、スレッドがロックを取得することができ、残りのスレッドは待つことができます。
最も典型的には、それがロックを共有することができます読んで、それは本当に唯一のロックは排他的で書くことができ、読み取りロックでReentrantReadWriteLockあります。
私たちは、ロックを取得し、ロックを解除具体彼のコードを見て。
//获取锁指定离不开这个lock方法,
public void lock() {
sync.acquireShared(1);
}
//acquireShared()首先会通过tryAcquireShared()来尝试获取锁。
//如果说获取不到那么他就回去执行 doAcquireShared(arg);直到获取到锁才会返回
//你看方法名do是不是想到了do-while呢?
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
// tryAcquireShared()来尝试获取锁。
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
//只有这个方法获取到锁了才会进行返回
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
//上面的这些方法全部都是在AbstractQueuedSynchronizer中
//而他通过Sync来调用的acquireShared
//而Sync则是继承的AbstractQueuedSynchronizer
abstract static class Sync extends AbstractQueuedSynchronizer
而他调用的tryAcquireShared则是在ReentrantReadWriteLock中
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
//获取状态
int c = getState();
//如果说锁状态不是0 并且获取锁的线程不是current线程 返回-1
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
//统计读锁的次数
int r = sharedCount(c);
//若无需等待,并且共享读锁共享次数小于MAX_COUNT,则会把锁的共享次数加一,
//否则他会去执行fullTryAcquireShared
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
return fullTryAcquireShared(current);
}
/** fullTryAcquireShared()会根据是否需要阻塞等待
读取锁的共享计数是否超过限制”等等进行处理。
如果不需要阻塞等待,并且锁的共享计数没有超过限制,
则通过CAS尝试获取锁,并返回1。*/
final int fullTryAcquireShared(Thread current) {
/*
* This code is in part redundant with that in
* tryAcquireShared but is simpler overall by not
* complicating tryAcquireShared with interactions between
* retries and lazily reading hold counts.
*/
HoldCounter rh = null;
for (;;) {
int c = getState();
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
// else we hold the exclusive lock; blocking here
// would cause deadlock.
} else if (readerShouldBlock()) {
// Make sure we're not acquiring read lock reentrantly
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
} else {
if (rh == null) {
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
}
}
if (rh.count == 0)
return -1;
}
}
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) {
if (sharedCount(c) == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
if (rh == null)
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
cachedHoldCounter = rh; // cache for release
}
return 1;
}
}
}
ロック取得処理複数のソースコードは、ロックを共有されています
その後のロックを解除するようにしてください
unlock()
public void unlock() {
sync.releaseShared(1);
}
//和获取锁的过程类似,他首先会通过tryReleaseShared()去尝试释放共享锁。尝试成功,则直接返回;尝试失败,
//则通过doReleaseShared()去释放共享锁。
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
//是尝试释放共享锁第一步。
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count;
}
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
// Releasing the read lock has no effect on readers,
// but it may allow waiting writers to proceed if
// both read and write locks are now free.
return nextc == 0;
}
}
//持续执行释放共享锁
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
上記のコードは、ロックロックソースを共有および非共有されています。実際には、中ReentrantReadWriteLockに定義されているそのうちのいくつかはここでは混乱、およびいくつかの方法がでAbstractQueuedSynchorizerに定義されているので、特に注意がコード見て前後にトグルする際、間違えないことに注意してください。
概要
排他ロック:1つのスレッドだけがロックを獲得します。
共有ロック:あなたがロックを取得、同時に複数のスレッドを持つことができます。
排他ロックと共有ロックでは、あなたは私を理解できますか?
Java技術オタク公共番号は、Java技術の開発を愛する人々のグループによって設立された、オリジナル、高品質のJavaの記事の共有に焦点を当てました。あなたは私たちの記事が悪いわけではないと思われる場合は、サポートを転送し、見て、感謝助けてください、より良い記事を共有するために私たちを励まします。いかなる社会的関心は、我々は必見のJava /情報インタビューの公開、自由に利用可能な知識に「公園をブログ」、いいえ背景を返信することはできません。