リエントラントロック(再帰的ロック)ReentrantLockの実装:排他ロック(ロック公正かつ不公平ロック)

はじめに
Java言語は、ArrayBlockingQueue、CopyOnWriteArrayListと、LinkedBlockingQueue、スレッドセーフではありませんsynchronizedキーワードによってそれらの実施など、多くのネイティブデータ構造のスレッドセーフを、持っていますが、java.util.concurrent.locks.ReentrantLockによって達成されます。ただ、非常にこのの詳細な分析ブログを書くので、このに興味がある「原則のリエントラントロック実現。」
ReentrantLockのは、その内部クラスFairSync(公正ロック)と達成されNonFairSync(不当なロック)に基づいて実装されています。これは、再入可能性はに基づいにThread.currentThread()の実装である:現在のスレッドがロックの実行順序を受信した場合、実行シーケンスのすべてのメソッドがロックを取得することができた後ということ。

フェアロック:

公正かつ不公平なロックキューが二重にリンクされたリストロック内部メンテナンスに基づいており、値は各要求は、現在のスレッドをロックされているノードテーブルノード。公正をロックするたびに、そのチームからの最初の注文の値。
Lock実装は、以下の点に基づいています。
状態テーブルは、揮発性のキーワードをノードとノードのステータス。
sum.misc.Unsafe.compareAndSetアトミック操作(付録を参照してください)。
アンフェアロック:

ロック待ちのプロセスでは、任意の新しいスレッドがロックを獲得しようとするとがある場合、ロックを直接得るための素晴らしいチャンスがあります。
開発者が独自の割り込みビットを設定しない限り、** ReentrantLockのロックは、スレッドの割り込みを行っていません。
ReentrantLockのの取得がスピンするように見えるが、それはスピンロックではありませんロックコード。
ReentrantLockの株式及び非株式ロックは排他ロックに帰属します。

ReentrantLockの再入の分析は、
ロックの1組がロックでブログ-Java非常に詳細な記事朱のサーバントをご紹介があります。

同期再入可能性は
同期ロックリアライズ:この記事を参照してください。

ロックのさまざまなレベルで同期似ているの両方の基本的な原理は、リエントラント実装は同じではありません。

バイアスロック:スレッドIDマークWordの記録。
軽量ロック:現在のスレッドにロックのレコードのマークWordがポイント。
ヘビー級のロック:マークのWord _recursions疲れて増加コントラストポイントのロック、ロックが減少します。
スレッドオブジェクトのスレッドによって保持された現在のロックに基づいてReentrantLockの、状態が蓄積又は退行されます。ヘビー級のロックの実装のような、より。

再入のReentrantLockの
彼が言及した序文、ReentrantLockのリエントラントをベースにThread.currentThread()の実装である:現在のスレッドがロックを取得し、このスレッド内のすべてのメソッドがロックを取得できることをしている場合。ReentrantLockのロックは、彼らが買収をロックするのとほぼ同じ方法、のみNonfairSyncとFairSync 2つの実装クラスを依存しています。

次のコードの再入ベースは、if文、他のスニペット

tryAcquireへの最終ブールは、保護された(int型買収){
    最終的にThread.currentThreadスレッド電流=();
    int型getStateをC =();
    IF(C == 0){
        ...
        //ロックを取得するために成功した試み
    }
    他のIF(現在の== getExclusiveOwnerThread()){
        //直接ロックに現在のスレッド、。再入可能性を実現。
        買収NEXTC = C + INT;
        IF(NEXTC <0)
            スロー新しい新しいError( "COUNTロックが最大数を超えた");
        SETSTATE(NEXTC);
        trueに復帰;
    }
    falseに復帰;
}

二つの値がありますが、ここで心配する必要があります。

    / **
     。MODE同期の排他的な所有者で*現在
     *現在のスレッドがロックを保持
     * /
    プライベート過渡をスレッドexclusiveOwnerThread。

     -----------------二つの値が同じクラス----------------ではありません
     
    / **
     状態の*同期。
     * 0を:初期状態-任意スレッドせずにロックされた
     *> 0:スレッドは、特定の値を保持している現在のスレッドによって保持されている実行回数を表す。
     * 
     *このフィールドは、また、使用する必要がロックを解除するための時間です。
     *なお、この修飾子フィールド:揮発性
     * /
    プライベート揮発int型国家、


ReentrantLockのは、分析的ロック達成
公正かつ不公平ロックロック
ReentrantLockのフェアや不公平ロックロックを取得するためにAbstractQueuedSynchronizer番号取得要求を委託。

決勝のボイド獲得パブリック(int型のArg){
    IF(tryAcquireへ(アルギニン)&&!
        acquireQueued(addWaiter(Node.EXCLUSIVE)、アルギニン))
        selfInterrupt();
}

tryAcquireには抽象メソッドで、*** **公正かつ不公平です*実装原理の嘘。
addWaiterノードがキューで待機するために、現在のスレッドに追加されます。ロック解除後のフェアロックは、その後の値に厳密に従ってもらうためにキューされるまで待機します、というよりも来スレッドの公正でロックは大きな利点があります。
ロックまたは複数のサイクルでブロックされ、現在のスレッドを取得しようとする試みをacquireQueued。
selfInterruptスレッドは中断がコールにThread.currentThread()の間に発生するブロックされた場合。割り込み()現在のスレッドに割り込み。
スレッドをブロックするReentrantLockのがLockSupport.park(本)に基づいている;(AbstractQueuedSynchronizer#parkAndCheckInterruptを参照されたいです)。前提条件は、現在のノードが失敗したロックを獲得する試みを制限されることです。

ロックロック公正かつマルチスレッド環境の有無ロックのコアを取得することが保証されて揮発性のキーワード修正された状態フィールドを使用するに乗ると言っにおける不公平。
同時複数のスレッドが状態== 0を読み出す場合しかし、その後、CAS技術を使用する必要があり、CPU原子は、技術は、データ変更操作の不可分、共有変数の形でCPUによってロックすることができるロック。
CASと揮発性の組み合わせは、プリエンプト同時の鍵となります。

FairSyncフェアロック
公正なロック機構を実現するには、彼らはそれが再び何のキューが存在しないチェックして、そうであれば、現在のスレッドは、次の手順を実行することを、ロックをつかむために、スレッドがあるたびということです。

IF(hasQueuedPredecessors()&&!
    {compareAndSetState(0、買収))
    setExclusiveOwnerThread(現在の);
    trueに復帰;
}

待ちキューがあるかどうかをチェックするためのhasQueuedPredecessors。

    最終的なブールhasQueuedPredecessorsパブリック(){
        ノードT =尾; //読む注文リバース初期のフィールド
        ノードH =ヘッドと、
        ノードS;
        !リターンT = H &&
            ((S = h.next)== nullを|| s.thread !=にThread.currentThread());
    }

不公平ロックNonfairSync
つかむランダムの実現に不公平なロックを繰り返し強調しました。

IF(C == 0){
    IF(compareAndSetState(0、取得)){
        setExclusiveOwnerThread(現在);
        真への復帰;
    }
}

ロックを取得するために来るのプロセスがロックをつかむために多くの機会を有することが公平ロック差。待ち時間がキューに追加された場合、公正ロックと違いはありません。

ReentrantLockのロック解除
すべてのメソッドがロックを解放するシーン内のロックされるまで待たなければならない、再入シナリオで言うことですReentrantLockのロックが段階的にリリースされたリリースを、現在のスレッドは、ロックがされる保持しますリリース!
非常に単純な方法を解放し、状態フィールドが1減少することができます。

最終的なブールtryReleaseは、保護された(int型のリリース){
    //リリース1 =。
    int型getStateをC =() -リリース;
    IF(!にThread.currentThread()= getExclusiveOwnerThread())
        スロー新しい新は、IllegalMonitorStateException();
    falseにブール=無料;
    IF(C 0 ==){
        無料= trueに、
        setExclusiveOwnerThread(NULL);
    }
    SETSTATE(C);
    戻り無料;
}

ReentrantLockのキューウェイクアップ要素を待ち
、現在のスレッドがロック後にロックを解除され、ロック不当無線プリエンプション処理した場合、それをスタートスレッドは、プロセスを起動します。
ロックを解除することによりTryReleaseの成功は、LockSupport.unpark(s.thread)を呼び出し、ブロックされたスレッドを終了します。
コードを参照してください。

ボイドunparkSuccessorプライベート(ノードノード){
    //強制ライトバック状態がスレッドを覚醒される
    INT WS = node.waitStatus;
    IF(WS <0)
        compareAndSetWaitStatus(ノード、WS、0);
    node.nextノードS =;
    //次のノード時間Sおよび一般に非ヌルである
    IF(S == NULL || s.waitStatus> 0){
        S = NULL;
        //そうでない場合、FIFO原理に従ってキャンセル最初のキューを検索しないようにノード
        のために(T =ノード尾; !! = nullを&& T T =ノード; T = t.prev)
            IF(t.waitStatus <= 0)
                S = T;
    }
    //それを覚ます再び
    もし(!S =ヌル)
        LockSupport.unpark(s.thread);
}


ReentrantLockのメモリ可視分析の
次のコードのために:

{試し
    Lock.lock();
    iが++;
} {最後に
    ()をlock.unlock;
}

私は複雑な問題でない場合要素I、さらに揮発性キーワードを使用せずに見ることができる修飾されました。

CASと揮発性のJava並行性基礎
Javaで揮発性キーワードは、関数が変更された要素(共有変数)ことを保証することです。

強制的にメインメモリ内から採取し、このプロセスは、保持された変数を共有クリアする読み出し時の任意のプロセスの値、、;
任意のプロセスが完了した書き込み時には、共有変数の書き込みとメインメモリの値を強制します。
揮発性のコマンド転位を介入します。
揮発性が起こる-前JMM仕様の原則を達成するために。

マルチコアCPUには、再注文の指示にコンパイラを許可するために、プログラムの正しいセマンティクスを確保するの前提の下で、CPUの命令実行速度を向上させるためには、環境をマルチスレッド。コードの上位層に並べ替え、この命令は知覚できないことを、我々はプロセッサの順序を呼び出します。

プログラマが明示的にこの再配置介入メカニズムなど揮発性により、適切に実行するために、そのマルチスレッドコードを確実にするために、同期メカニズムを確立しない限り、JMMは、自由遊びでコンパイラディレクティブ重いラフトすることができます。Javaの並行処理:揮発性メモリの可視性とコマンドの並び替えの記事を参照してください。

スレッドの相互Aの複数間のデータ依存性がある場合は、明示的に再配置メカニズムの介入を指揮しなければなりません。

CASは、CPUが提供する技術です。シングルコアプロセッサ上でスレッド、すべての命令が順次動作するように許可されている;しかし、マルチコア、マルチスレッド・プロセッサ、マルチスレッドアクセス同じ共有変数に、並行性の問題が存在してもよいです。

CAS技術が生きた要素の値を使用してロックすることができます。Intelは第VIII章の、文書を開発し
、コンパイラは、スレッドロックされた値が保持している値を比較し、更新と同じ値が更新されます。
CASはまた、JMMが起こる-前に仕様の原則に従います。
JAVA CASの原則深さ分析のブログを見て

ロックロック公正かつマルチスレッド環境の有無ロックのコアを取得することが保証されて揮発性のキーワード修正された状態フィールドを使用するに乗ると言っにおける不公平。
同時複数のスレッドが状態== 0を読み出す場合しかし、その後、CAS技術を使用する必要があり、CPU原子は、技術は、データ変更操作の不可分、共有変数の形でCPUによってロックすることができるロック。
CASと揮発性の組み合わせは、プリエンプト同時の鍵となります。

JSR-133コンパイラマニュアル書く
反復の数世代を経てJMMの仕様を、JSR-133は、スペックのより一般的なバージョンです。

書き込みコンパイラマニュアルドキュメントを参照してください:コンパイラの作家のためのJSR-133クックブック(非公式ガイド)

それを回避する方法、上記の命令の小さなセクションでは、揮発性の再配置オフ避けるべき?

両方の4つの組み合わせのリード/ライト動作以外の何ものでもありませんメモリを、読み取りまたは書き込み中:

LoadStore
LoadLoad
StoreStore
StoreLoad
命令が並べ替えられる方法を防ぐために、キーワード「メモリバリア」を提供することにより揮発性、揮発性メモリのセマンティクスを達成するために、生成バイトコード命令シーケンスメモリバリアに挿入され、コンパイラは特定のタイプを阻害しますプロセッサを並べ替えます。ほとんどのプロセッサは、メモリバリア命令をサポートしています。

揮発性の読み出し動作は、バリアLoadLoadの後ろに挿入されています。
揮発書き込み動作StoreLoadバリアの後ろに挿入されています。
それは何を使用することである。このStoreLoad / LoadLoadそれ?インテル・デベロッパドキュメント、VIII章を参照してください。簡単に言えばStoreLoadスレッド・キャッシュは、フォローアップの命令がメモリに書き戻されトリガされ、そしてLoadLoadは、処理のために内部に格納され、主読み出したデータから再スレッドをトリガします。

マルチプロセッサシステムにおける同期メカニズムは、強いメモリ順序付けモデルに依存し得ます。ここで、プログラムはメモリ上のリードモディファイライト動作がアトミックに行われることを保証するようXCHG命令またはLOCKプレフィックスとしてロック命令を使用することができます。彼らは完全に、メモリに排水するバッファリングされた全ての書き込みのために以前のすべての指示を待つことにロック操作は、一般的にI / O操作のように動作します。

メモリのReentrantLockの可視性
上のブログでは、次のとおりです。公正で公平な分析#のReentrantLockのロックロックロックを達成し、非言及する:ReentrantLockのは、CASと揮発性のミックスでロック機能を実現します。

尚、状態フィールドvolatileキーワード変更された状態のフィールドは読み取りと一緒に、書面でロック解除に続くメモリバリアの保証ReentrantLockのメモリの可視性を構成しています。このメモリバリアは、ReentrantLockの視認性を確保します

CASに似ては、原則として揮発性メモリバリア
(E)は、Javaメモリモデルの記事の深い理解を参照してください-ロック
、次の文書の抜粋

揮発性メモリバリア機能は、Javaのコンパイル時、追加バイトコードによって達成されます。

unsafe.cpp> atomic.cpp> atomicwindowsx86.inline.hpp:CAS JNIは、ローカルのJavaコードUnsafe.javaにレベル呼び出しによって呼び出されます。コードは、ケースを呼び出します。

#define LOCK_IF_MP(MP)__asm CMP MP、0 \
                       __asm JE L0 \
                       __asm _emit 0xF0が\
                       __asm L0:

原子:: JINT A CMPXCHGインライン(JINT exchange_value、揮発性JINT * DEST、JINTのCOMPARE_VALUE){
  //代替InterlockedCompareExchangeため
  INT is_MP :: MP = OS();
  __asm {
    MOV EDX、DEST
    MOV ECX、exchange_value
    MOV EAX、COMPARE_VALUE
    LOCK_IF_MP( MP)
    CMPXCHG DWORD PTR [EDX]、ECX
  }
}

ソースコード、上記のように、プログラムは、現在のプロセッサのタイプに基づいてプレフィックスCMPXCHGロック命令を追加するかどうかを決定します。プログラムは、プレフィックスロック(ロックCMPXCHG)を追加するには、マルチプロセッサ上で実行されている場合CMPXCHG命令です。プログラムは、単一のプロセッサ上で実行されている場合は逆に、省略されているロックの接頭辞(シングルプロセッサのメモリバリア自体はシングルプロセッサのための効果の一貫性を維持し、接頭辞は、ロックを提供する必要はありません)。

次のような手順については、インテルのマニュアルは、接頭辞をロック:

変更- -書き込みアトミック実行メモリを読み取ることを確認してください。他のプロセッサは、バスを介してアクセス・メモリに一時的にできなくなるように、以前のPentiumプロセッサおよびPentiumにおいて、接頭辞ロックコマンドは、バスの実行中にロックされます。明らかに、これは高価なオーバーヘッドをもたらすでしょう。あなたがプロセッサですでにロックプレフィックス命令にアクセスしたい場合は、実行メモリ領域(メモリ領域)の間に:インテルPentium 4プロセッサ、インテルXeonプロセッサ、およびP6プロセッサ、非常に重要に最適化された上で、元のバスロックに基づいてインテルからスタート内部キャッシュがロックされている(すなわち、現在の排他的または変更状態のキャッシュラインにおけるメモリの領域を含む)、メモリ領域全体が単一のキャッシュライン(キャッシュライン)内に含まれ、プロセッサは直接命令を実行します。キャッシュラインは常に命令の実行中にロックされますので、他のプロセッサがアクセスするメモリ領域へ/書き込みコマンドを読み取ることができない、それはアトミックの実行を保証することができます。この操作は、キャッシュが大きくロックプレフィックス命令を実行するコストを削減するが、複数のプロセッサまたは命令間の競争度の高いメモリアドレスにアクセスしたときにロックが整列されていない、キャッシュロック(キャッシュロック)と呼ばれ、依然としてバスロックされます。
読み出し、書き込みは、並べ替えの前と後のコマンド命令で禁止されています。
バッファメモリ内のすべてのデータをリフレッシュするために書きます。
十分な揮発性読み出し及び書き込み揮発性メモリセマンティクスを達成しながら、第二の点及び第三の点より、メモリバリア効果を有します。
 

オリジナル:https://blog.csdn.net/qyp199312/article/details/70598480

公開された740元の記事 ウォン称賛65 ビュー10万+

おすすめ

転載: blog.csdn.net/qq_41723615/article/details/104347634