Java読み書きロックReentrantReadWriteLockのロック降格例

17295 ワード

前に書き込みロックを解読したとき、読み取り共有、読み書き反発しか知らなかった.
スレッドが書き込みロックを取得してから読み取りロックを取得すると、書き込みロックが解放されます.このプロセスをロックダウングレードと呼びます.
現在のスレッドがリードロックを取得してからライトロックを取得すると、デッドロックが発生します.
この時、小さな頭がブーンと鳴り始めた.
  • ロックはなぜ降格できるのか、アップグレードできないのか.
  • ロックダウングレードはどのシーンで発生しますか?

  • 質問1:ロックはなぜダウングレードできるのか、アップグレードできないのか.
    まず、リードロックは複数のスレッドによって保持することができるが、ライトロックは同じ時点で1つのスレッドによってのみ保持される.この理論に基づいて例を挙げます.私たちは読み書きロックが反発していることを知っています.読むときは書けません.
  • では、10個のスレッドがリードロックを取得したと仮定します.このとき、10個のスレッドがライトロックにアップグレードされなければなりません.このとき、問題が発生しました.あなたはまだリードロックを解放していません.取得する書き込みロックは待機キューに並ぶしかなく、この10スレッドが読み取りロックを解放するのを待っていますが、この10スレッドは書き込みロックを取得するときにデッドロックになっているため、解放できません.

  • ここで少しまとめてみます.
  • 読んでいる間は書けません.マルチスレッドの場合、書き込みロックは待ち行列の読み取りロックの後ろに並び、読み取りも解放されていないので、書き込みロックを取得すると、デッドロックになります.
  • しかし書く時読むことができて、書くロックは1つのスレッドにしか取得できないため、
  • 質問2:ロックがダウングレードされるシーンは何ですか?
    例を挙げると、トランザクションの実行に10秒かかり、書き込みに1秒かかり、その他は読み取り9秒であると仮定します.複数のスレッドが読み書きロックを競合する場合:非降格
  • Aスレッドは、この書き込みロックを取得して処理を開始し、最後に解放されると、時間は10秒となり、他のスレッドが10秒ブロックされる可能性があります.
  • Aスレッドは書き込み時のみ書き込みロックをかけ、他の読み取りの場合はロックをかけないと書き込みロックが解放されたばかりで、別のスレッドがデータの変更を開始する可能性がありますが、A側はデータの不一致を引き起こす可能性があります.
  • Aは、書き込みロックを解除してから、読み取りロックを取得する.他のスレッドが書き込みロックを奪った可能性があり、A側が読み取りロックを取得する際にブロックが発生し、トランザクションの進行を中断した.

  • ロックダウングレードの場合、Aがライトロックを取得して1秒のライト操作を行った後、リードロックにダウングレードし、ライトロックを解除する.ケース1:待ち行列が先頭にあるのがリード・ロック・スレッドであると仮定すると、Aがライト・ロックを解除した後、待ち行列内のリード・ロック・スレッドが伝播して起動し、それぞれのタスクの実行を開始する.ケース2:ただし、待機キューが1位に書き込みロックスレッドがある場合は、申し訳ありませんがブロック中です.Aはまだリードロックを解除していないため、Aは次の操作を継続することができ、トランザクションの連続性が中断されません.
    トランザクションの整合性を保証するには、書き込みロックだけで行うと、時間がかかりすぎて、書き込みロックを解放してから読み取りロックを取得します.読み取りロックを取得すると、他のスレッドが書き込みロックを奪っている可能性がありますが、読み取りロックは待つしかありません.この時、あなたの事務は中断されただけでなく、実行時間が長くなる可能性があります.ロックダウンの助けで,ケース1の優位性が発揮された.
    まとめ
  • 時間がかかるトランザクションでは、ロックのダウングレードの利点:
  • は、書き込みロックの占有時間を短縮する.
  • スレッドの同時性が
  • 増加
  • で、トランザクションが中断されないことを保証します.

  • 検証されたコードブロックを添付します.
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    public class ReadWriteLockTest {
        private Logger logger = LoggerFactory.getLogger(ReadWriteLockTest.class);
        ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
    
    	/**
    	 *	              ,                     。 
    	 */
        public void queryData() {
            try {
                Thread.sleep(500);
                readLock.lock();
                logger.info("      .");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                readLock.unlock();
            }
    
        }
    
        public void test3() throws Exception {
            //      
            writeLock.lock();
            logger.info("       ...");
           	//                            .
            Thread.sleep(500);
    		//                       
            processReadLock(1); //        processWriteLock  
    		
            processWriteLock(2);
    
            Thread.sleep(500);
    		//      
            readLock.lock(); // A     
            //      
            writeLock.unlock();
            logger.info("       ");
            queryData();
            readLock.unlock(); // A     
            logger.info("       ");
            logger.info("    ..");
        }
    
        private void processWriteLock(int threadIndex) {
            new Thread(() -> {
                logger.info("  " + threadIndex + "       ,   .");
                writeLock.lock();
                logger.info("  " + threadIndex + "      ..");
                writeLock.unlock();
                logger.info("  " + threadIndex + "     ..");
            }).start();
        }
    
        private void processReadLock(int threadIndex) {
            new Thread(() -> {
                logger.info("  " + threadIndex + "       ,   .");
                readLock.lock();
                logger.info("  " + threadIndex + "      ..");
                readLock.unlock();
                logger.info("  " + threadIndex + "     ..");
            }).start();
        }
    
    
        public static void main(String[] args) throws Exception {
            ReadWriteLockTest readWriteLockTest = new ReadWriteLockTest(); 
            readWriteLockTest.test3();
        }
    }
    

    印刷結果:
           ...
      1       ,   .  		//                 .
      2       ,   . 			//           
           					//         ,        
      1      ..				//            
      1     ..					//      .
          .						//             
           
        ..						//        
      2      ..				//     ,           
      2     ..
    

    ここでは、私が予測したいいくつかの状況を説明します.メインスレッドはまず書き込みロックを奪い、それからリードロックに降格します.ロックのダウングレードに成功した後:
  • プライマリ・スレッドは現在、リード・ロック・スレッドにあります.キュー内のリード・ロックが起動するのを待っていますか?//答えは目覚める
  • 待ち行列に並んでいるのが書き込みスレッドであれば、私のメインスレッドの書き込みロックが先に解放されたときに、奪われますか?//答えはできません.メインスレッドのリードロックがまだ解放されていないからです.

  • ここでテストしたのは、ロックがダウングレードされた場合です.
  • 待機キューの一番前が書き込みスレッドである場合、プライマリスレッドの読み書きロックが解放されるのを待たなければならない場合、待機キューに渡されません.
  • 待ち行列の先頭がリードスレッドである場合、プライマリスレッドはリードロックを解放し、起動します.

  • 上記の観点が間違っている場合は、コメントを残して検討し、共同で進歩してください.ありがとうございます.