16.Java_ロックシステム

11954 ワード

一、ロックプロフィール(JDK 1.5提供)
ロック:複数のスレッドが共有リソースにアクセスするように制御します.
  • ロックインタフェースが現れる前にjavaプログラムはsynchronizedキーワードでロック機能を実現する.
  • Lockインタフェースはsynchronizedキーワードのような暗黙的なロック解除の利便性を失ったが、ロック取得と解放の操作性、中断可能な取得ロック、タイムアウト取得ロックなどの同期特性を持っている.
  • synchronized同期ブロックの実行が完了した場合、または異常が発生した場合、ロックは自動的に解放され、ロックはunlock()解放ロックを呼び出す必要があります.
  • Lock lock = new ReetrantLock();
    try{
    	lock.lock();
    	//              
    	...
    }finally{
    	lock.unlock();//    
    }
    

    二、lock共通API
    lockシステムは、取得ロックの中断、タイムアウト取得ロック、共有ロックなどの特性を有する.
  • void lock();//取得ロック
  • void lockInterruptibly() throws InterruptedException();//レスポンス割り込みロック
  • boolean trylock();//取得ロックはtrueを返し、逆にfalse
  • を返す.
  • boolean trylock(long time,TimeUnit unit);//タイムアウト取得ロックは、所定時間内にロックが取得されずfalse
  • に戻る.
  • Condition newCondition();//ロックにバインドする待機通知コンポーネント
  • を取得する
  • void unlock();リリースロック
  • Lockインタフェースの実装サブクラスReetrantLockのすべてのメソッドは、実際には静的内部クラスSyncのメソッドを呼び出し、SyncはAbstractQueuedSynchronizer(AQS-同期器と略称)を継承する
         implements Lock{
    	lock();
    	unlock();
    
    	static class Sync extends AbstractQueuedSynchronzer{
    
    	}
    }
    

    三、AQS-シンクロナイザ
    定義:ロックおよび他の同期コンポーネントを構築するための基礎フレームワーク.彼の実装は主にint状態変数に依存し、FIFOキューを介して同期キューを構成する.
  • サブクラスは、AQSのprotected修飾の同期状態を変更する方法を書き換える必要があります.他の方法は、主にキューとブロックメカニズムを実現しています.int状態の更新にはgetState()、setState()およびcompareAndSetState()が使用されます.
  • サブクラスは静的内部クラスを使用してAQSを継続して独自の同期の意味を実現することを推奨する.同期器は排他ロックも共有ロックもサポートする.
  • ロックとAQSの関係:
  • ロックは、ユーザに対して、ユーザがロックと対話するインタフェースを定義する.
  • 同期器はロックの実現に向けて、ロックの実現方式を簡略化し、同期状態管理を遮蔽し、スレッドのキュー、待機、起動などの操作を行う.
  • 四、AQSのテンプレートモード
    定義:AQSはテンプレートメソッドモードを使用して、いくつかのステータスに関連するコアメソッドをサブクラス書き換えに開放し、その後、AQSはサブクラス書き換えのステータスに関するメソッドを使用してスレッドのキュー、ブロック、起動などの操作を行います.
  • AQSはテンプレートメソッドを用いて設計されており、AQSのprotected修飾のメソッドはAQSを継承するサブクラスによって書き換えられる必要があり、AQSのサブクラスのメソッドを呼び出すと書き換えられるメソッドが呼び出される.
    五、AQS詳細
    役割:同期コンポーネントの中で、AQSは最も核心的な部分であり、同期コンポーネントの実現はAQSが提供するテンプレート方法に依存して同期コンポーネントの意味を実現する.
  • AQSは、同期状態の管理、およびブロックスレッドのキュー化、通知待ちなどの下位層の実装を実現する.
  • .
  • AQSコア構成:同期キュー、排他ロックの取得と解放、共有ロックの取得と解放、割り込み可能ロック、タイムアウトロック.この一連の機能の実現はAQSが提供するテンプレート方法に依存する.
    1、排他ロック
    (1)void acquire(int arg);独占的に同期状態を取得し、取得に失敗した場合に同期キューを挿入して待機する.(2)void acquireInterruptibly(int arg);(1)に基づいて、この方法は同期キューで割り込みに応答することができる.(3)boolean tryAcquireNanos(int arg,long nanostimeOut);(4)boolean tryAcquire(int arg);殴り合いロックはtrueに成功し、そうでなければfalseに戻る.(5)boolean release(int arg);同期状態を解放すると、同期キューにある次のノードが起動する.
    2、共有ロック
    (1)void acquireShared(int arg);共有取得同期状態、同時刻に複数のスレッドが同期状態を取得する.(2)void acquireSharedInterruptibly(int arg);(1)に基づいて応答割り込みを増加する.(3)boolean tryAcquireSharedNanos(int arg,long nanostimeOut);(2)に基づいてタイムアウト待ちを増加する.(4)boolean releaseShared(int arg);共有で同期状態を解放します.
    3、同期キュー
    AQS内部には、同期キュー内の各特定のノードである静的内部クラスNodeがある.
    ノードには次のプロパティがあります.
  • int waitStatus:ノード状態
  • Node prev:同期キュー内の前駆ノード
  • Node next:同期キュー内の後継ノード
  • Thread thread:現在のノードパッケージのスレッドオブジェクト
  • Node nextWaiter:待機キュー内の次のノード
  • ノードのステータス値は次のとおりです.
  • int INITIAL=0;//初期状態
  • int CANCELELED=1;//現在のノードが同期キューから
  • をキャンセル
  • int SIGNAL=-1;//後継ノードが待機している.現在のノードが同期状態を解放すると後継ノードに通知され、後継ノードが
  • を継続する.
  • int CONDITION=-2;//ノードは待機キューにあります.他のスレッドがConditionに対してsignal()メソッドを呼び出すと、ノードは待機キューから同期キューに移動します.
  • int PROPAGATE=-3;//共有同期状態は無条件に伝播する
  • AQS同期キューには、ヘッダ・エンド・ノード付きの双方向チェーン・テーブルが使用されます.
    六、排他ロックの取得:acquire(int arg)
    ロックの取得に失敗した後にAQSが提供するacquire(int arg)テンプレートメソッドを呼び出す
  • tryAcquire(arg):同期状態の取得を再試行し、直接メソッドの終了に成功し、addWaiter()の呼び出しに失敗しました.
  • addWaiter(Node.EXCLUSIVE)、arg):現在のスレッドを指定したモード(排他的、共有的)でノードにカプセル化した後、同期キューに挿入します.
  • private Node addWaiter(Node node){
    	Node node = new Node(Thread.currentThread(),node);
    	Node pred = tail;
    	if(perd != null){
    		node.prev = pred;
    		if(compareAndSetTail(pred,node)){
    			pred.next = node;
    			return node;
    		}
    	}
    	enq(node);
    	return node;
    }
    
  • enq(Node node):現在のキューが空またはCASエンドプラグに失敗した場合、このメソッドを呼び出してキューを初期化またはエンドプラグを切断しない.
  • private Node enq(final Node node) { 
       for (;;) {
       		Node t = tail;     
            if (t == null) { // Must initialize            
            //        
            if (compareAndSetHead(new Node()))
                tail = head;        
            } else {            
              	node.prev = t;            // CAS  ,              。            
              	if (compareAndSetTail(t, node)) {                
              		t.next = node;                
              		return t;            
              	}        
             }    
         } 
     }
    
  • acquireQueued()ロック成功条件:ノードがエンキューされた後、キューに並んで同期状態を取得します.現在のノードの前駆体はヘッダノードであり、同期状態の再取得に成功しました.
  • ノード同期キューでロックを取得し、失敗した後にshouldParkAfterFailedAcquire(Node prev,Node node)を呼び出すこの方法の主なロジックは、CASを使用して前駆ノードの状態をSIGNALに設定し、現在のノードをブロックする必要があることを示すことです.CASが失敗すると、前駆ノードの状態がSIGNALになるまでスピンを続けます.
  • acquireQueued(); 1.現在のノードの前駆ノードがヘッダノードであり、同期状態の取得に成功した場合、現在のスレッド取得ロックは成功し、メソッドは終了する.2.ロックの取得に失敗した場合、前駆ノードのステータスをSIGINALにしてから、LockSupport.park()メソッドを呼び出して現在のスレッドをブロックします.
  • ノード同期状態への先行条件:現在のノードの前駆ノードがヘッダノードであり、tryAcquireを呼び出して同期状態->前駆ノードデキュー
  • を取得する.
    排他ロックの解放:release()
    unlock()メソッドAQSが提供するrelease()テンプレートメソッドを実際に呼び出す
  • release()メソッドはunlock()メソッドの具体的な実装である.まず、ヘッダノードの後続ノードを取得し、後続ノードがnullでない場合、LockSupport.unpark()メソッドを呼び出して後続ノードパッケージのプロセスを起動します.したがって、ロックが解放されるたびに、キュー内のノードの後続ノードがパッケージされたスレッドが呼び出されます.

  • 排他ロックの取得と解放の概要:
    1.スレッド取得ロックに失敗し、スレッド呼び出しaddWiter()をNodeにカプセル化してエンキュー操作を行う.addWriter()のメソッドenq()は、同期キューのヘッダノードの初期化およびCASエンドプラグインの失敗後の再試行処理を完了します.2.エンキュー後にキューに並んでロックを取得するコアメソッドacquireQueued()であり、ノードに並んでロックを取得することはスピンプロセスである.現在のノードの前駆ノードがヘッダノードであり、同期状態が正常に取得された場合にのみ、ノードがデキューされ、ノードが参照するスレッドがロックを取得する.そうでない場合、条件が満たされていない場合、常にスピンして前駆ノードの状態をSIGNALに設定し、LockSupport.park()を呼び出して現在のスレッドをブロックします.3.ロックを解除すると、後続ノードが起動します(後続ノードはnullではありません).
    七、独占ロックの特性
    1.ロック取得時の応答中断:
    原理はacquire()とほぼ同じであり,parkAndCheckInterrupt()がtrueを返すとスレッドがブロックされていることを示し,割り込み異常を投げ出すとスレッドが終了する.
    2.タイムアウト待ちロック:
    tryAcquireNanos()は、タイムアウト時間内に現在のスレッドがロックを正常に取得したことを3つの場合に返します.現在のスレッドはタイムアウト時間内に中断されてタイムアウト時間が終了し、ロックは取得されず、スレッドはfalseに戻ります.
    まとめ:タイムアウト取得ロックロジックは、割り込み可能な取得ロックとほぼ一致します.唯一の違いは、ロックの取得に失敗した後、時間処理が追加されたことです.現在の時間がカットオフ時間を超えると、スレッドは待機せず、直接終了しfalseに戻ります.そうでない場合は、スレッドブロックを待機状態キュー取得ロックに設定します.