マルチスレッドのロックとsynchronizedの比較と使用

6049 ワード


まず両者の違いを比較する.
カテゴリ
synchronized
Lock
階層の存在
Javaのキーワードは、jvmレベルで
クラスjavaです.util.concurrent.locks
ロックの解除
1、ロックを取得したスレッドは同期コードを実行し、ロックを解除する2、スレッド実行に異常が発生し、jvmはスレッドにロックを解除させる
一般にtry()catch{}で使用され、finallyでロックを解除する必要があります.そうしないと、スレッドのデッドロックが発生しやすくなります.
ロックの取得
Aスレッドがロックされ、Bスレッドが待機していると仮定します.Aスレッドがブロックされている場合、Bスレッドは待機します.
場合によっては、ロックには複数のロックを取得する方法があります.
普通はロックを使えばロック()ロックは、1つのスレッドが別のスレッドを占有している場合でも、現在のスレッドがロックを解除するのを待ってから実行する必要があります.
ただし、tyLock()ロックを使用している場合、1つのスレッドがロックを占有している場合、他のスレッドは待たずに他のことをしたり、戻ったりすることができます.
ロック状態
判断がつかない
判断できる
tryLock()メソッドには戻り値があります.これは、ロックの取得を試みるために使用され、取得に成功するとtrueを返します.取得に失敗すると(つまり、ロックが他のスレッドによって取得された)、falseを返します.つまり、このメソッドはどうしてもすぐに戻ります.ロックが取得されない場合、そこで待機することはありません.
tryLock(long time,TimeUnit unit)メソッドはtryLock()メソッドと似ていますが、このメソッドはロックが取れない場合に一定の時間待ち、時間内にロックが取れない場合はfalseに戻ります.最初にロックを取得したり、待機中にロックを取得したりした場合はtrueに戻ります.
ロックの種類
再入不可中断不可不公平
再入可能判断可公平(両方可)
パフォーマンス
少量の同期(コンカレント量が小さい場合は効率的)
大量同期(コンカレント量が大きい場合は効率も良く、コンカレント量が小さい場合は
synchronizedの違いは大きくありません
)
第二:synchronizedを使用する場合は、次の点に注意してください.
synchronizedを使用する場合、ロックフラグは必ずstaticで修飾され、どのスレッドがアクセスしても同じフラグが使用されます.
メソッドボディにもコードブロックにも使用できます
例:
                  private static Object object = new Object();//staticで飾る
                  private static int piaoshu = 50;//staticで修飾する
第三:クラスjavaを使用する.util.concurrent.ロック中のロックロック
     
一:まずReentrantLockロックについて説明します
            private static ReentrantLock lock = new ReentrantLock();
ロックロックを使用する場合は、グローバルに宣言する必要があります.ローカルに宣言すると、各スレッドが使用時にnewのReentrantLockオブジェクトが出てくるので、各スレッドが同じReentrantLockロックを使用していない場合は、ロックを追加することはありません.
ReentrantLockの方法はどれらがありますか.最もよく使われるのは以下のいくつかです.
lock()、tryLock()、tryLock(long time,TimeUnit unit)、lockInterruptibly()解放所の方法はunLock()
         1.まずlock()メソッドは,通常最も多く用いられるメソッドであり,ロックを取得するために用いられる.この方法を使用してスレッドをロックする場合、synchronizedと同様に、1つのスレッドがロックを取得した後、他のスレッドは、このスレッドの解放を待つ必要があります.
        2.1つのスレッドがロックを取得した後、他のスレッドを待たせたくない場合は、tryLock()、tryLock(long time,TimeUnit unit)の2つのメソッドのいずれかのロックを使用します.いずれのロックにも戻り値trueまたはfalseがあります.tryLock(long time,TimeUnit unit)メソッドは、スレッドをどのくらい待たせた後にロックが取得されていない場合は、戻ったり、他の操作をしたりします.
        3.彼らの使用は一般的にtry()catch{}で使用され、lockにのみ使用される.lock()とlock.unlock()の中間のコードはロックするが、それ以外のコードはロックしないので、AとBの2つのスレッドが同時に実行されると、Aが先にロックを取得すると、Bはロックを取得しないので、Bはロックを実行できない.unlock()以降のコードは、Aが実行されてからBがロックをかけてロックを解除してから次のコードを実行するしかありません.これがコードの実行順序の問題です.前のコードが実行されていないと、後のコードは実行できません(非同期と新しいスレッドを除く)
        4.lock.lock()の使用方法は、ロックを取得するために使用されます.ロックが他のスレッドによって取得された場合、他のスレッドは待機します.
  
      lock.lock();
        try{
            //    
                     
        }catch(Exception ex){
                
        }finally{
            lock.unlock();   //   
        }

        5.tryLock()とtryLock(long time,TimeUnit unit)の使用.ここでtryLock(時間を表し、時間単位はミリ秒、ナノ秒、秒を表す)
tryLock()メソッドには戻り値があります.これは、ロックの取得を試みるために使用され、取得に成功するとtrueを返します.取得に失敗すると(つまり、ロックが他のスレッドによって取得された)、falseを返します.つまり、このメソッドはどうしてもすぐに戻ります.ロックが取得されない場合、そこで待機することはありません.
tryLock(long time,TimeUnit unit)メソッドはtryLock()メソッドと似ていますが、このメソッドはロックが取れない場合に一定の時間待ち、時間内にロックが取れない場合はfalseに戻ります.最初にロックを取得したり、待機中にロックを取得したりした場合はtrueに戻ります.
したがって、通常tryLockによってロックを取得する場合は、次のように使用されます.
boolean tryLock = lock.tryLock();
  boolean tryLock = lock.tryLock(long time, TimeUnit.MILLISECONDS);
        ,          (    ,    )
if(tryLock ) {
     try{
         //    
     }catch(Exception ex){
             
     }finally{
         lock.unlock();   //   
     } 
}else {
    //       ,        
}

            6.その他の方法
isHeldByCurrentThread():現在のスレッドがロックされているかどうかを問い合わせる
isFair():ロックがフェアロックかどうかを判断する
isLocked():lockが任意のスレッドによって所有されているかどうかをクエリーします.      
二:ReentrantReadWriteLock読み書きロックReadWriteLockインタフェースを実現
                1.ReentrantReadWriteLockには豊富な方法がたくさんありますが、最も主要な方法はreadLock()とwriteLock()がリードロックとライトロックを取得するために使用されています.
                2.ファイルの読み取りと書き込みでsynchronizedロックを使用する場合、両方のスレッドがファイルの読み取りである場合、1つのスレッドが別のスレッドを占有する場合は、読み取り効率が低下するのを待つ必要があります.読み取りロックを使用すると異なります.
            3.使用方法
            
public class Test {
    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
     
    public static void main(String[] args)  {
        final Test test = new Test();
         
        new Thread(){
            public void run() {
                test.get(Thread.currentThread());
            };
        }.start();
         
        new Thread(){
            public void run() {
                test.get(Thread.currentThread());
            };
        }.start();
         
    }  
     
    public void get(Thread thread) {
        rwl.readLock().lock();
        try {
            long start = System.currentTimeMillis();
             
            while(System.currentTimeMillis() - start <= 1) {
                System.out.println(thread.getName()+"       ");
            }
            System.out.println(thread.getName()+"     ");
        } finally {
            rwl.readLock().unlock();
        }
    }
}

thread 1とthread 2が同時に読み取り動作を行うことを示す.
これにより、読み取り操作の効率が大幅に向上します.
ただし、1つのスレッドがリード・ロックを占有している場合、他のスレッドがライト・ロックを申請する場合、ライト・ロックを申請するスレッドは、リード・ロックの解放を待機していることに注意してください.
スレッドが書き込みロックを占有している場合、他のスレッドが書き込みロックまたは読み取りロックを申請すると、申請したスレッドは書き込みロックの解放を待機します.
 4.ロックとsynchronizedの選択
まとめると、ロックとsynchronizedには以下の点があります.
1)Lockはインタフェースであり、synchronizedはJavaのキーワードであり、synchronizedは内蔵言語実装である.
2)synchronizedは、異常が発生すると、スレッドが占有するロックが自動的に解放されるため、デッドロック現象が発生しない.ロックは異常が発生した場合、unLock()を介してロックを解除しなければ、デッドロック現象を引き起こす可能性が高いため、ロックを使用する場合はfinallyブロックでロックを解除する必要がある.
3)Lockはロック待ちのスレッド応答を中断させることができるが、synchronizedはだめで、synchronizedを使用すると、待機しているスレッドはずっと待っていて、中断に応答できない.
4)ロックが成功したかどうかをロックで知ることができるが,synchronizedではできない.
5)ロックは、複数のスレッドによる読み取り操作の効率を向上させることができる.
性能上、競合リソースが激しくなければ、両者の性能はそれほど悪くないが、競合リソースが非常に激しい場合(すなわち、大量のスレッドが同時に競合している場合)、ロックの性能はsynchronizedよりはるかに優れている.したがって、具体的な使用時には適切な状況に応じて選択しなければならない.