I.はじめに
一般的に私たちは私たちが一般的にReentrantLockの例に使用し、ロック条件を使用して、
ReentrantLockのロック=新しいReentrantLockの(); 条件条件= lock.newCondition()。 lock.lock(); {試みる condition.awaitを(); }最後に{ lock.unlock()。 } lock.lock()。 {試みる condition.signal()。 }最後に{ lock.unlock()。 }
上から知ることができる、我々はとき)condition.awaitで、その後、上記の手順(、最初にすべての私たちは、それがロックを取得するために取得する方法であるかどうか、基礎としてこれを使用し、条件を呼び出し、信号方式は、ロックを取得する場合に取得する必要が待っています通話condition.signal()メソッドは、のawaitされることに時間を呼び起こすかもしれないスレッドをブロックし、現在のスレッドの呼び出しをブロックし、私はそれが必ずしも可能ではない、ここで言います。なぜあなたは、メソッドのawait()で見てみましょうは、主に何かをする、と言いますか。
第二に、分析
以下はのjdk8のawaitメソッドの源であります
下面是のawait方法的在jdk8的源码、 公共の最後の空のawait()がスローInterruptedExceptionある{ 場合(Thread.interrupted())//① )(新しい例外:InterruptedExceptionを投げます。 ノードノード= addConditionWaiter()。//② INT savedState = fullyRelease(ノード)。//③ INT interruptMode = 0; (!isOnSyncQueue(ノード)){//④しばらく LockSupport.park(この); (もし!(interruptMode = checkInterruptWhileWaiting(ノード))= 0)//⑤ 休憩。 } 場合(acquireQueued(ノード、savedState)&& interruptMode!= THROW_IE)//⑥ interruptMode = REINTERRUPT。 キャンセル⑦もし(!node.nextWaiter = NULL)//クリーンアップであれば unlinkCancelledWaiters(); (!interruptMode = 0)//⑧場合 reportInterruptAfterWait(interruptMode)。 }
実装プロセスは、以下の通りである。
現在の表示が割り込みであるかどうかを判断するために①、?はい、それは割り込み例外InterruptedExceptionある、スローされます
、現在のスレッドのデータノードノードと②、コールaddConditionWaiter方法主に新しいものを、次に条件条件キューに追加します。
③、fullyReleaseメソッド呼び出し、メソッド呼び出しリリース方法の内部、及び方法がevokeスレッドが(存在する場合ヘッド後継ノード)AQS内部キュー内の最初のノード(次ノードの、すなわち、ヘッド)を放出する主です。この場合、2活性シンクロナイザASQスレッド内に少なくとも存在する(ヘッドノードとすることができる現在のスレッド()が、他のスレッドが(存在する場合)誘発しました)。あなたが喚起に失敗した場合は、例外は、IllegalMonitorStateExceptionがスローされます。注:スレッドが呼び起こすとき、スレッドが勝つためにロックを取得することができます。
④、キュー内のノードisOnSyncQueue AQSかどうかを決定する方法により、場合呼のawait時間、確かではないAQSに、新しいaddConditionWaiter新しい方法がノードであるからである。次に、whileループの内部に入ります。コールLockSupport.park(この);ロックを解除するために同等の、現在のスレッドをブロックします。
⑤、誘発現在のスレッド(最初のノードが別のスレッドが現在のノードfirstWaiterことは、先行ノードがキャンセルした場合、このスレッドによって、又は信号の第1のノードを誘発することができる待つよい)、必要中断ブレークが、そうでなければ0を返すをcheckInterruptWhileWaiting場合、中断interruptModeに格納されているかどうかを決定する、ノードは、ノードAQSがない場合、まだ場合、またはwhileループのうち、現在のスレッドを駐車するかどうかを決定します。次いで、ノードはAQSに追加されるノードは、アンサー信号方式です。
⑥、ときキューAQS内のノードノード、我々はロックを戦うために、AQSキュー上のノードノードの現在のスレッドのみ、ロックを取得する必要があります。AcquireQueued方法はロックを呼び出すことです。分析は、他の方法をダウンacquireQueued。
⑦、node.nextWaiterは、現在のノードが状態条件のために他のスレッドであることを示す、空でない場合、現在のスレッドがロックを獲得しており、そのノードのタイプにキュー状態を解除クリア
、⑧場合interruptMode InterruptedExceptionあるタイプまたはタイプREINTERRUPT 。その後、適切な例外や割り込みスレッド自己割り込みフラグビットがセットされているスローします。
次に、信号を解析する方法
公共の最終無効信号(){ 場合(isHeldExclusively()!)//① )(新しいは、IllegalMonitorStateExceptionを投げます。 ノード最初= firstWaiter。 (!最初= null)の場合 doSignal(最初の); //② }
実装プロセスは以下の通りであり、
現在のスレッドがロックの状態により保持されていない場合①、その後は、IllegalMonitorStateExceptionの実装は例外をスローします。
②、doSignal方法、および最初のノード受信キューの状態を呼び出します。
プライベート無効doSignal(ノードが最初){ {行う 場合((firstWaiter = first.nextWaiter)== NULL)//① lastWaiter = NULL; first.nextWaiter = NULL; }ながら(!transferForSignal(最初の)&&(第一= firstWaiter)= NULL); //② }
ノードキューの現在の状態を除去する必要があり、次のノードがnullに空、次いでlastWaiter設定されているため①、この状態firstWaiterキューポインタ次のノード、空の状態キューは、説明first.nextWaiter =ヌル、続いて記載されています状態キューを削除し
、ループしながら②、二段階の操作がある場合、1 transferForSignal、第二、第= firstWaiter、AQSキューに我々の現在の最初のノードが成功した場合、真リターンtransferForSignal、doSignalの端
現在最初場合ノードに障害が発生したにAQSは、AQSキューに成功したfirstWaiterノードを確保、再信号を次のノードの必要性を返します。その後、主に何もしていているtransferForSignal方法を見てください。
最終的なブールtransferForSignal(ノードノード){ / * *場合はwaitStatusを変更することはできません、ノードがキャンセルされました。 * / (!compareAndSetWaitStatus(ノード、Node.CONDITION、0))//①の場合 のリターンはfalse; / * *スプライスキューへとに前身のwaitStatusを設定してみてください *そのスレッドが待っている(おそらく)であることを示します。キャンセルまたは場合 *セットwaitStatusへの試みが失敗し、再同期に目を覚ます(ここで *場合waitStatusは一過性と無害に間違っている可能性があります)。 * / ノードP = ENQ(ノード)。//② int型WS = p.waitStatus。 もし(WS> 0 ||!compareAndSetWaitStatus(P、WS、Node.SIGNAL))//③ LockSupport.unpark(node.thread)。 trueを返します。 }
①、最初の着信ノードのノードが直接falseを返す、(外側が除去された)変更が失敗した場合、ノードは、ノードの条件ではないことを示し、その状態条件、初期状態0を変化させる第1のノードのキュー状態です、 AQSキューにdoSignal方法リコールfirstWaiter新しいノード、
②、AQSノードへの最初のノード、ENQ()メソッドの戻りノードに先行ノード。ここからコアの方法は、信号の役割を知ることができる()メソッドは、AQSのキューの末尾に非同期転送ノードをキャンセルするために待ち行列における最初です。遷移後のノードが同期しているキューを詰まらせる可能性がある、と目覚めたときに、その先行ノードがヘッドノードであるかどうかに応じ。
パーク③、現在のノードの前駆体はwaitStatus> 0(命令は状態をキャンセルしている)、前駆体ノードがCanncelは、(説明の先行ノードが中断された、など)、あなたがevokeスレッド、その後のawait方法LockSupport.unpark(node.thread)を呼び出すことができていれば戻り値は、すぐに返しに行くために裁判官の失敗は、最適化に役割を果たしたときに同期ステータスを得るために待つことなく、以前に相殺AQS同期キューノードを削除することができます。
acquireQueuedを分析するための最後のロックの取得方法
以下の操作を行います。
最終的なブールacquireQueued(最終ノードノード、INTのarg){//① 失敗=真のブール。 {しようと 、中断ブール=偽; 用(;;){ 最終ノードp = node.predecessor()。 もし(P ==ヘッド&& tryAcquire(引数)){//② setHead(ノード)。 p.next = NULL; //ヘルプGCは = falseを失敗しました。 リターンが中断しました。 } IF(shouldParkAfterFailedAcquire(P、ノード)&& parkAndCheckInterrupt())//③ 中断= TRUE。 } }最後に{ (失敗した)場合 cancelAcquire(ノード)。 } }
①、ノードニーズへの着信ノードは状態前の状態のロック、引数を取得する。
②、入ってくる前駆体ノードがヘッドノードであれば、現在のノードがキューAQSノード最初のものであることを示す、あなたは我々そのロックを取得しようとすることができます私たちは、ロックを取得するために成功した同期のセマンティクス場合は、最初のノードの現在のヘッドノードを設定し、セマンティックアプローチtryAcquireを達成することが同期させる必要があります。
リターンではなく、成功を得るためのロックかどうか、割り込みが発生するかどうかの意味論の価値があることをここで注意してください。
③、必要公園スレッドは、スレッドは、割り込みフラグがあるか否かが判定された場合、現在のスレッドの障害が発生した後、ロック公園を取得するかどうかを決定するために使用されるコールshouldParkAfterFailedAcquire方法。
その後、我々はshouldParkAfterFailedAcquire方法を見てください。
shouldParkAfterFailedAcquireブールプライベート静的(ノードは、ノードノード、predを){ int型WS = pred.waitStatus。 もし(WS == Node.SIGNAL)//① / * *このノードには、すでにリリースを求めてステータスを設定している 、それが安全に駐車することができますので、それを合図します*。 * / trueを返します。 もし(WS> 0){//② / * *前身はキャンセルされました。前任者の上にスキップして *再試行を示しています。 * / 実行{ node.prev = PRED = pred.prev。 }一方(pred.waitStatus> 0)。 pred.next =ノード。 {}他 / * * waitStatusは0またはPROPAGATEなければなりません。私たちがいることを示す *信号を必要とするが、まだ駐車しないでください。発信者は、にする必要があります 確認してください、それは駐車場の前に取得することはできません作成する*再試行。 * / compareAndSetWaitStatus(predは、WS、Node.SIGNAL)。//③ } falseを返します。 }
①waitStatusは代表は、現在のスレッドを駐車する必要があり、先行ノードは、trueを返し、SIGNALである現在のノードが有効であることを説明しpredが
ループのロックを獲得し続けて外の先行ノードがキャンセルされた②、前駆体ノードは、リターンがfalseの場合、削除をキャンセルする
に③そして、その後、waitStatusの先行条件文のノード0、または広がり伝播しなければなりません、SIGNALにセット伝播ノードであり、偽を返し、知っての失敗は、SIGNALとしてshouldParkAfterFailedAcquire前駆体を呼び出すようにする場合と、ループの取得のためにロックを行く、と(前③による設定)は、スレッドが駐車する必要があります。
ここからは、transferForSignal方法,! CompareAndSetWaitStatus(P、WS、知ることができます Node.SIGNAL) ステートメントを使用すると、その前身のノードに障害が発生したNode.SIGNALを設定した場合、あなたがスレッドを駐車するかどうかを判断するacquireQueuedまで待つ必要はありません、スレッドが直接unparkをすることができ