synchronizedとjava.util.concurrent.locks.ロックの違い

5618 ワード

ConcurrentHashMapソースコードを見ているとlockというロックメカニズムが見え、Synchronizedとの違いが分からず、いくつかの資料を調べて記録したが、Lockのドキュメントでは、synchronizedよりもロックの実現が追加のロック操作を提供し、より生き生きとした構造があり、異なる属性をサポートすることができ、複数の関連条件のオブジェクトをサポートすることができると説明している.では、ロックがsynchronizedよりも追加の操作を提供できるかどうかを見てみましょう.つまり、synchronizedに存在する問題を解決します.1、ロックを待っているスレッドを中断することはできません.2、投票でロックを得ることもできません.待たなければロックを得ることはできません.最初の問題はlockInterruptibly()メソッドを使用します.lockInterruptibly()メソッドは、ロックが別のスレッドに保持されていない場合、ロックを取得してすぐに戻り、ロックの保持カウントを1に設定します.現在のスレッドがこのロックを保持している場合、保持カウントは1に加算され、メソッドはすぐに返されます.ロックが別のスレッドに保持されている場合、現在のスレッドはスレッドスケジューリングの目的で無効になり、次の2つの状況のいずれかが発生するまで、スレッドはスリープ状態になります.a、ロックは現在のスレッドによって取得されます.b、または他のスレッドが現在のスレッドを中断する.現在のスレッドがロックを取得した場合、ロック保持カウントは1に設定されます.現在のスレッド:a、このメソッドに入ったときにスレッドの割り込み状態が設定されている場合.b、またはロックの取得を待つ間に中断される.InterruptedExceptionが放出され、現在のスレッドの割り込み状態がクリアされます.すなわちlockInterruptibly()メソッドは、待機中に他のスレッドから呼び出すことを可能にする.interruptメソッドは、待機を中断して直接戻り、ロックを取得せずにInterruptedExceptionを放出します.2番目の問題はtrylock()メソッドまたはtryLock(long timeout,TimeUnit timeUnit)メソッドを使用します.trylock()メソッド:ロックを取得したらすぐにtrueに戻り、他のスレッドがロックを持っている場合はfalseに戻ります.tryLock(long timeout,TimeUnit timeUnit)メソッド:ロックを取得するとすぐにtrueに戻り、他のスレッドがロックを持っている場合、パラメータが与えられた時間を待つ.待機中、ロックを取得するとtrueに戻り、待機タイムアウトが発生するとfalseに戻る.1つの例を見てみましょう.適切ではないスコアを打ってください.あなたは今仕事に忙しいので、急に内急を感じて、トイレに走って、入り口に行って「清掃中、使用を一時停止」するカードを見つけました.仕方がなくて、仕事はまた忙しくて、だからあなたは先にトイレに行って帰って仕事を忙しくすることを放弃するしかなくて、このように缲り返して、ついにあなたは入ることができることを発见して、そこで......このようなシーンをsynchronizedでどうやって実現しますか?仕方ありません.synchronizedでは、トイレが一時的に入れないことに気づいたら、おとなしく入り口で待つしかありません.trylock()を使うには、まずトイレに行ってみて、しばらく入ることができないことに気づきました(trylockはfalseに戻ります)、トイレに入ることができるまで仕事を続けます(trylockはtrueに戻ります).さらに、あなたはとても急いでいて、入り口で20秒待ってみることができて、仕事を忙しくすることはできません(trylock(20,TimeUnit.SECONDS);).
synchrozized:
現代のコードブロックにsynchrozizedを加えると、2つの変更があります.一つは原子性(atomicity)、一つは可視性(visibility)である.原子性は、一度に1つのスレッドしかコードロックを取得できず、synchronizedに囲まれたコードブロックに入って実行されることを意味する.可視性は、異なる範囲の変数の変更に対する一貫性です.Javaメモリでは、メモリキャッシュとコンパイラの最適化がマルチスレッド条件下で様々な異常な動作をもたらすため、変数の可視性と一貫性を強調します.一般に、スレッドは、レジスタ(register)においてもプロセッサ固有のキャッシュ(CPU cache)においても、命令再配置によっても、または他のコンパイラによっても、他のスレッドがすぐに見ることができるようにする必要はないが、キャッシュ変数値の制約を受けないが、synchronizedキーワードを追加すると、ライブラリを実行すると、あるスレッドが変数に対する更新が既存のsynchronizedブロックに対する更新よりも先に行われることを保証し、同じモニタで保護された別のsynchronizedブロックに入ると、これらの変数に対する更新がすぐに表示されます.同様の規則はvolatile変数にも存在する.
Lock:
JDK 1より.5 Javaはjavaを提供していると思っています.util.concurrentというパッケージは、そのサブパッケージlocksにおいて、ロックに関する抽象的なクラスを提供しています.2つのロックがあります.ReentrantLock、ReentrantReadWriteLockです.ReentrantLockロックの例の実装は以下の通りである.
Lock lock = new ReentrantLock();
try {
    lock.lcok();
    // do something
} finally {
    lock.unlock();
}

上記のコードでは、まずlockを作成し、lock()メソッドを呼び出し、ロックをオンにし、最後にunlock()を呼び出してロックを解除します.注意すべき時、一般的にロックを使用する時、上のスタイルでコード、すなわちlockを書くべきである.unlock()はfinallyブロックに置くことが望ましい.これにより、do somethingの実行中に異常が発生した後、ロックが永遠に解放されないことを防止することができる.
ReentrantReadWriteLockの使い方はReentrantLockと基本的に同じですが、ReentrantReadWriteLockは特殊なルール(読み書きロック)を実現しているだけで、ReentrantReadWriteLockには2つの内部クラスReentrantReadWriteLockがあります.ReadLockとReentrantReadWriteLock.WriteLock(実際には2つの内部クラスだけでなく、AbstractQueuedSynchronizerを実現するSyncなど)は、ReentrantReadWriteLockのreadLock()とwriteLock()をそれぞれ使用して返すことができ、この読み書きロックのルールは、writerがない限り、読み取りロックは複数のreaderスレッドで同時に保持され、書き込みロックは独占的である.ReentrantReadWriteLockロックの例:
package test.mult;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
* @ClassName: ReadWriteLockTest
* @author whwang
* @date 2012-1-11  02:20:59
 */
public class ReadWriteLockTest {

    static ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);

    public static void main(String[] args) {
        //  reader -  

        //  writer -  

        //  reader , writer  -  

        //  writer , reader  -  

        MyThread t1 = new MyThread(0, "t1");
        MyThread t2 = new MyThread(0, "t2");
        MyThread t3 = new MyThread(1, "t3");
        t1.start();
        t2.start();
        t3.start();
    }

    private static class MyThread extends Thread {

        private int type;

        private String threadName;

        public MyThread(int type, String threadName) {
            this.threadName = threadName;
            this.type = type;
        }

        @Override
        public void run() {
            while (true) {
                if (type == 0) {
                    // read
                    ReentrantReadWriteLock.ReadLock readLock = null;
                    try {
                        readLock = lock.readLock();
                        readLock.lock();
                        System.err.println("to read...." + threadName);
                        try {
                            Thread.sleep(5 * 1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } finally {
                        readLock.unlock();
                    }
                } else {
                    // write
                    ReentrantReadWriteLock.WriteLock writeLock = null;
                    try {
                        writeLock = lock.writeLock();
                        writeLock.lock();
                        System.err.println("to write...." + threadName);
                        try {
                            Thread.sleep(5 * 1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } finally {
                        writeLock.unlock();
                    }
                }
            }
        }
    }
}