Javaにおけるロックの概念の概要


文書ディレクトリ
  • フェアロック/非フェアロック
  • フェアロック
  • 非公平ロック
  • 楽観ロック/悲観ロック
  • 楽観ロック
  • 悲観ロック
  • 排他ロック/共有ロック
  • 排他ロック(排他ロック)
  • 共有ロック
  • 反発ロック/読み書きロック
  • 反発ロック
  • 読み書きロック
  • 偏向錠/軽量級錠/重量級錠
  • 偏向ロック
  • 軽量ロック
  • ヘビー級ロック
  • 再入ロック
  • セグメントロック
  • スピンロック
  • ロック粗化
  • ロック消去
  • フェアロック/非フェアロック
    フラットロック
    フェアロックとは、マルチスレッド環境で申請ロックの順序でロックを取得することであり、FIFOキューのように、先にロックを申請したスレッドがリソースがある場合に先にロックを取得することを保証する.例では、フェアロックについて説明します.
    public class FairLockDemo {
        //        
        static class FairLockRunnable implements Runnable {
            ReentrantLock reentrantLock = new ReentrantLock(false);//  false      ,       
    
            @Override
            public void run() {
                String threadName = Thread.currentThread().getName();//      
                reentrantLock.lock();//   
                try {
                    System.out.println("[" + threadName + "]    ");
                    Thread.sleep(2000);//      , 2s
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    reentrantLock.unlock();//   
                    System.out.println("[" + threadName + "]    ");
                }
            }
        }
    
        public static void main(String[] args) {
            ExecutorService executor = Executors.newCachedThreadPool();//       
            for (int i = 0; i < 5; i++) {
                executor.execute(new FairLockRunnable());
            }
            executor.shutdown();//       
        }
    }
    

    印刷結果:
    [pool-1-thread-2]    
    [pool-1-thread-1]    
    [pool-1-thread-4]    
    [pool-1-thread-3]    
    [pool-1-thread-5]    
    [pool-1-thread-2]    
    [pool-1-thread-1]    
    [pool-1-thread-4]    
    [pool-1-thread-3]    
    [pool-1-thread-5]    
    

    ここでロックを取得してロックを解放する順序は、ロックを申請する順序に従っていることがわかります.これが公平ロックです.new ReentrantLock(false)は公平なロックです.
    ノンフェアロック
    非フェアロックはフェアロックに対応し,マルチスレッド環境でロックを取得する順序は申請ロックの順序では行われない.テストコードは上記と同じで、1つの場所を変更するだけです.
            ReentrantLock reentrantLock = new ReentrantLock();//  false      ,       
    

    印刷結果:
    [pool-1-thread-1]    
    [pool-1-thread-2]    
    [pool-1-thread-3]    
    [pool-1-thread-4]    
    [pool-1-thread-5]    
    [pool-1-thread-2]    
    [pool-1-thread-3]    
    [pool-1-thread-4]    
    [pool-1-thread-1]    
    [pool-1-thread-5]    
    

    ロックを取得する順序は,ロックを申請する順序ではなく,実際にはランダムであることがわかる.非フェアロックは、リソースを取得したスレッドにランダムに実行させるため、ビジネスのスループットを増加させることができますが、優先度の反転やスレッドの飢餓の問題を引き起こす可能性があります(ロックが取得されず、待機している可能性があるスレッドがある可能性があります).そのため、不公平なロックです.
    楽観ロック/悲観ロック
    楽観ロックと悲観ロックは、問題を見る方法のように、楽観ロックは楽観的にデータを見る傾向があり、取得するたびに1つのスレッドしかないと判断し、悲観ロックは悲観的にデータを見る傾向があり、取得するたびに複数のスレッドがあると判断しているため、データロックを1つのスレッドにのみアクセスさせる必要がある.
    楽観ロック
    楽観ロックは、マルチスレッド同時問題がないと常に考えられているため、データを取得するたびに他のスレッドがデータを修正しないと考えられているため、ロックされません.ただし、更新時にデータの比較を行い、他のスレッドによって変更されないようにします.インプリメンテーションは、一般に「データ・バージョン・メカニズム」または「CASアクション」を使用して実現されます.
  • データバージョンメカニズムは、データベース・テーブル・フィールドにversionフィールドを追加することです.このフィールドは、変更された回数を表し、データが変更されるたびにversionが1ずつ加算されます.変更するときは、クエリーを取得したversionとデータベースに存在するversionが等しく更新されるのを待たなければなりません.そうしないと、更新に失敗します.
  • CAS(Compare and Swap)を比較して交換する.CASには、メモリ値(V)、比較を行う予想値(A)、修正する予定の新しい値(B)の3つのオペランドがあります.メモリ位置Vの値が予想値Aと一致すると、比較器は新しい値をBに更新し、そうでなければ処理しない.

  • Javaでjava.util.concurrent.atomicパッケージの下にある原子変数クラスは、楽観的ロックを用いた実装方式CAS(Compare and Swap比較および交換)によって実装される.
    悲観ロック
    悲観的なロックは、データが常にマルチスレッドにアクセスされていると考えられ、データを操作する場合はロックをかけて実装する必要があります.データ操作の前にロックをかけ、操作が完了したり、操作が異常になったりしてもロックを解除します.悲観ロックにはsynchronized、ReentrantLockなどがある.
    排他ロック/共有ロック
    排他ロック
    排他ロックとは、ロックリソースが1つのスレッドによって保持され、毎回1つのスレッドしか排他的に使用できないことを意味します.ReentrantLocksynchronizedはいずれも独占ロックである.
    共有ロック
    共有ロックとは、ロックリソースが複数のスレッドによって保持されることを意味する.スレッドAがデータDataに対して共有ロックをかけた場合、他のスレッドはDataに対して共有ロックを追加するしかなく、排他ロックを追加することはできません.排他ロックと共有ロックはJAVAでAQSによって実現される.ReentrantReadWriteLockリードロックは共有ロックであり、ライトロックは独占ロックである.リードロックの共有は、同時読み取りが効率的であることを保証し、読み書き、書き込みは反発します.
    反発ロック/読み書きロック
    反発ロック
    反発ロックとは、最大1つのスレッドしか持たないロックで、独占ロックの1つです.ReentrantLockおよびsynchronizedは、いずれも反発ロックである.
    リードライトロック
    読み書きロックとは、読み書きに関する具体的なロックの一種で、独占ロックと共有ロックがあり、readモードは共有されているが、writeモードは独占的である.読み書きロックの仕組み:
  • 「読み-読み」反発しない
  • 「読み書き」反発
  • 「書く-書く」反発
  • JAvaにおける読み書きロックの実装はReentrantReadWriteLockである.
    偏向ロック/軽量ロック/ヘビー級ロック
    この3つのロックは、ロックの状態を指し、Synchronizedの場合である.Java 5では,ロックアップグレードのメカニズムを導入することで効率的なSynchronizedを実現する.この3つのロックの状態は、オブジェクトモニタのオブジェクトヘッダ内のフィールドによって示されます.JVMは以下の方法で偏向ロックをオフにします.
    -XX:-UseBiasedLocking
    

    バイアスロック
    バイアスロックとは、同期コードが常に1つのスレッドにアクセスされている場合、スレッドが自動的にロックを取得することを意味します.ロックを取得するコストを削減します.
    軽量ロック
    軽量ロックとは、ロックがバイアスロックである場合、別のスレッドにアクセスされると、バイアスロックが軽量ロックにアップグレードされ、他のスレッドがスピンの形でロックを取得しようとし、ブロックされず、性能が向上することを意味します.
    ヘビーロック
    ヘビー級ロックとは、ロックが軽量級ロックである場合、もう一つのスレッドがスピンであるが、スピンがずっと続くことはなく、スピンが一定回数ある場合、まだロックが取得されていない場合、ブロックに入り、このロックがヘビー級ロックに膨張することを意味する.ヘビー級ロックは、申請したスレッドをブロックし、パフォーマンスを低下させます.
    リエントロック可能
    再入可能とは、実は再利用可能という意味で、1本のロックを複数回繰り返すことができますが、ロックを取得した回数に応じてロックを解除する必要があります.つまり、ロックを追加するときは必ずロックを解除する回数に等しくしなければなりません.ReentrantLockおよびsynchronizedは、いずれも再ロック可能である.
    セグメントロック
    セグメントロックとは、本物のロックではない設計を指します.ConcurrentHashMapの場合、その同時実装は、セグメントロックの形式によって効率的な同時動作を実現することである.セグメントロックの設計目的は、ロックの粒度を細かくすることであり、操作が配列全体を更新する必要がない場合、配列の1つだけに対してロック操作を行う.
    スピンロック
    スピンロックは実際には本当のロックではなく、現象の記述にすぎず、原理は循環によって実現される.例えばJDKソースコードの多くはforループによってスピンロックを実現している.
    	for (;;) {
    		      ......
        }
    

    ロック粗化
    ロック粗化は、複数のロックを1つに統合し、重複ロック要求によるパフォーマンス損失を低減することを簡単に理解する.1つの例は次のとおりです.
    public void doSomethingMethod(){
       while(i<1000){
        	 synchronized(lock){
        		//do some thing
        	}
        }	
    }
    
    

    ロック粗化後のコード
    public void doSomethingMethod(){
        synchronized(lock){
        	while(i<1000){
        	//do some thing
        	}
        }
    }
    

    ロック解除
    ロック消去は、コンパイラレベルで発生するロック最適化方法です.簡単な例を挙げると理解できます.
        public synchronized void logApend(String content){
            StringBuffer sb = new StringBuffer();
            sb.append(content);
        }
    

    コードは非常に簡単で、StringBufferにコンテンツを追加する方法であり、この方法はsynchronizedで修飾されている.実は私たちはapppd方法のソースコードを見て、ここにもsynchronizedが修飾されていることを発見しました.
        @Override
        public synchronized StringBuffer append(String str) {
            toStringCache = null;
            super.append(str);
            return this;
        }
    

    コンパイラで最適化し、ロックを削除できます.Javaがサーバモードで実行される必要があることを前提とします(サーバモードはclientモードよりも最適化されます).同時に、脱出分析をオンにする必要があります.
    -server -XX:+DoEscapeAnalysis -XX:+EliminateLocks
    

    +DoEscapeAnalysis:脱出分析をオンにする+EliminateLocks:ロック解除.