[8] java.util.concurrentパッケージの一部のクラス

1.同期ロックロック、およびいくつかの一般的に使用される実装クラス

同期ロックロック、注(同期とは異なります):

1)とにかくロックを使用して手動でロックを解放する(そうしないとデッドロックが発生します)ので、finallyブロックに入れます。同期がブロックを離れると、ロックは自動的に解放され、通常はデッドロックは発生しません。

2)ロックにより、ロックを待機しているスレッドが中断に応答できるようになります。ロックを保持しているスレッドが長時間ロックを解放しない場合、ロックを待機しているスレッドは、他のことを行うために中断される可能性があります(ロックを介して)。 lockInterruptibly()メソッド)デッドロック回避します。ただし、同期は機能しません。同期を使用すると、待機中のスレッドは永久に待機し、割り込みに応答できなくなります。

lock.lockInterruptibly()メソッド:2つのスレッドがlock.lockInterruptibly()を介して特定のロックを同時に取得する場合、この時点でスレッドAがロックを取得し、スレッドBが再度待機する必要がある場合は、スレッドを待機します。この時間プロセスでBを中断できます。(スレッドがロックを取得しても、中断されることはありません。中断できるのは、ブロックプロセスのスレッドのみです。)

3)Lockを使用すると、ロックが正常に取得されたかどうかを確認できますが、同期では取得できません。lock.tryLock();(ロックが取得された場合はtrueを返し、そうでない場合はfalseを返します。待機時間内に取得された場合は待機時間を設定し、それ以外の場合はfalseを返します)

4)ロックは、複数のスレッドによる読み取り操作の効率を向上させることができます(ReadWriteLock)。

1.リエントラントロック

ReentrantLock(ReentrantLock):ReentrantLockはAQSに基づいています。再入可能とは、スレッドがロックを取得した後(ロック実行期間中)、スレッドが再びロックを取得して同期コードブロックに入ることができることを意味します。ここで強調する必要があるのは、再入可能性の概念で参照されるスレッドは、ロックを取得したスレッドです。これは、1つのスレッドのみがロックを取得できるため、スレッドセーフと競合しません。つまり、常に同じです。 thread(ロックを取得するスレッド)。)同期コードを実行する場合、シングルスレッド操作と同等であり、もちろんスレッドセーフです。synchronizedキーワードは、再入可能性もサポートします。たとえば、synchronizedは再帰呼び出しで使用できます。

ReentrantLockは、フェアロックとアンフェアロックを提供します(デフォルトはアンフェアロックです)

フェアロック:スレッドは、ロックを適用するための待機キューの順序でロックを取得します。(フェアロックとして指定:ロックロック= new ReentrantLock(true);)

不公平なロック:スレッドはランダムにロックを取得し、ロックを適用するための待機キューに従いません。

同期された実装は不公平なロックです。スレッドはランダムにロックを取得します。

ReentrantLockは、明示的な追加およびロック解除操作を提供します。ロック()、ロック解除操作を追加およびロック解除するためのlock()、unlock()メソッドを提供しますが、同期は暗黙的にロックおよびロック解除操作を実行します(コンパイラーによってmoniterenterおよびmoniterexitにコンパイルされます)。ReentrantLockのロックの待機は中断される可能性があります。ロックを保持しているスレッドが長時間ロックを解放しない場合、ロックを待機しているスレッドは待機を放棄することを選択できるため、同期によって発生する可能性のあるデッドロックの問題を回避できます。ReentrantLock.tryLock()は、待機時間を設定できます。

eg:

        while(true){

            lock.lock();

            try {

                if(tickets>0){

                    System.out.println(Thread.currentThread().getName()+"成功售票:"+tickets);

                    Thread.sleep(20);

                    tickets--;

                }else{

                    break;

                }

            } finally {

                lock.unlock();

            }

        }

ロックを使用してスレッドと通信する場合、Objectクラスのwait、notify、notifyAllの代わりに、Conditionのawait、signal、signalAllメソッドを使用する必要があります。

条件condition1 = lock.newCondition();

注意:

オブジェクトのnotify()メソッドは、オブジェクトのロックを待機しているスレッドをウェイクアップできます。複数のスレッドがオブジェクトのロックを待機している場合、ウェイクアップできるのはそのうちの1つだけです。どのスレッドがウェイクアップされているかは不明です。待機、通知、通知を使用するすべてのメソッドは、同期されたコードブロックまたは同期されたメソッドに含まれている必要があります

呼び出し条件のawait()、signal()、signalAll()メソッドは、ロックで保護する必要があります。つまり、Conditon Wait(のオブジェクトに対応するawait()を使用する前に、lock.lock()とlock.unlockの間にある必要があります。 ); Conditionのsignal()はObjectのnotify()に対応しますが、ウェイクアップするスレッドはsignalを使用して指定できます; ConditionのsignalAll()はObjectのnotifyAll()に対応します

2.読み取り/書き込みロック

スレッドが現在読み取りロックを占有している場合、書き込み操作を実行する必要があり、読み取りロックが解放されるのを待ってから書き込みロックを取得する必要があります。

スレッドが現在書き込みロックを占有している場合、スレッドは読み取り操作を実行する必要があり、読み取りロックを取得する前に書き込みロックが解放されるのを待つ必要があります。

例:ファイルの読み取りと書き込みのスレッドが複数ある場合、読み取り操作と書き込み操作は競合し、書き込み操作と書き込み操作は競合しますが、読み取り操作と読み取り操作は競合しません。

ただし、同期を実現するためにsynchronizedキーワードを使用すると、問題が発生します。

複数のスレッドが読み取り操作のみを実行している場合、1つのスレッドが読み取り操作を実行していると、他のスレッドは待機のみが可能で、読み取り操作は実行できません。

したがって、複数のスレッドが読み取り操作のみを実行している場合に、スレッド間で競合が発生しないようにするメカニズムが必要です。これは、ロックを介して実行できます。

さらに、ロックを介して、スレッドがロックを正常に取得したかどうかを知ることができます。これは同期ではできないことです

/**
* 读写锁:
* 写写、读写互斥,需要等待释放锁
* 读读不互斥,不需要释放锁
* @author JACK
*
*/
public class TestReadWriteLock {
    public static void main(String[] args) {
        ReadAndLockDemo demo = new  ReadAndLockDemo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                demo.set((int)(Math.random()*100));
            }
        },"write").start();
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    demo.get();
                }
            }).start();
        }
    }
}
class ReadAndLockDemo{
    private int number=0;
    ReadWriteLock readWriteLock = new  ReentrantReadWriteLock();
    //读
    public void get(){
        readWriteLock.readLock().lock();//上读锁
        try {
            System.out.println(Thread.currentThread().getName()+":"+number);
        } finally {
            readWriteLock.readLock().unlock();//释放读锁
        }
        
    }
    //写
    public void set(int number){
        readWriteLock.writeLock().lock();//上写锁
        try {
            System.out.println(Thread.currentThread().getName()+number);
            this.number=number;
        } finally {
            readWriteLock.writeLock().unlock();//释放写锁
        }
    }
}

3.閉鎖

/**
* CountDownLatch:闭锁,在进行计算时,只有在其他线程计算全部完成,当前运算才执行
* @author JACK
*
*/
public class TestCountDownLatch {
    public static void main(String[] args) {
        CountDownLatch latch = new  CountDownLatch(5);//参数表示?个线程完成后,主线程才运算
        LatchDemo latchDemo = new  LatchDemo(latch);
        long currentTimeMillis =  System.currentTimeMillis();
        for(int i=0;i<5;i++){
            new Thread(latchDemo).start();
        }
        try {
            latch.await();//等待其他线程上的运算完成,当前线程才可以进行运算
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long currentTimeMillis2 =  System.currentTimeMillis();
        System.out.println("耗费时间:"+(currentTimeMillis2-currentTimeMillis));
    }
}
class LatchDemo implements Runnable{
    private CountDownLatch latch;
    
    public LatchDemo(CountDownLatch latch) {
        this.latch=latch;
    }
    @Override
    public void run() {
        System.out.println("线程");
        try{
            for(int i=1;i<10000;i++){
                System.out.println(i);
            }
        }finally{
            latch.countDown();//线程数减1
        }
    }
    
}

 

おすすめ

転載: blog.csdn.net/Jack_PJ/article/details/88015515