ReentrantReadWriteLockシンプルな原則のケースプルーフ

目次

 

ReentrantReadWriteLockが存在するのはなぜですか?

排他的ロック取得の簡単なプロセス

ロックを共有する簡単なプロセス

書き込みロックの低下

総括する


ReentrantReadWriteLockが存在するのはなぜですか?


List、ArrayList、LinkedListの実装クラスはスレッドセーフではないことがわかっています。Vectorクラスは、同期された変更方法を使用してListのマルチスレッドの非安全性を保証しますが、読み取りと書き込みの同期という欠点があります。 、低効率そのため、CopyOnWriteArrayListが表示されます。これは、書き込み中に配列をコピーすることで読み取りと書き込みの分離を実現し、Listのマルチスレッド読み取りの効率を向上させ、ますます読み取りに適しています。同じ理由:ReentrantLockは排他ロックであり、スレッドの同期を制御するために使用されます。ReentrantLockを使用してArrayListセキュリティを実現する場合、CopyOnWriteArrayListと同じ効果を実現できますか?

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author :jiaolian
 * @date :Created in 2021-01-26 15:49
 * @description:ReentrantReadWriteLock多读少写的场景
 * @modified By:
 * 公众号:叫练
 */
public class MultReadTest {


    private static class MyList {
        private final ReentrantReadWriteLock REENTRANT_READ_WRITE_LOCK = new ReentrantReadWriteLock();
        private final ReentrantReadWriteLock.WriteLock WRITE_LOCK = REENTRANT_READ_WRITE_LOCK.writeLock();
        private final ReentrantReadWriteLock.ReadLock READ_LOCK = REENTRANT_READ_WRITE_LOCK.readLock();
        private List<String> list = new ArrayList();

        //读list
        public void readList() {
            try {
                READ_LOCK.lock();
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+":"+list.size());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                READ_LOCK.unlock();
            }
        }

        //写list
        public void writeList() {
            try {
                WRITE_LOCK.lock();
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+":新增1个元素");
                list.add("叫练【公众号】");

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                WRITE_LOCK.unlock();
            }
        }

    }

    public static void main(String[] args) {
        MyList myList = new MyList();
        //读写锁适合多读少写情况
        //新建10个读线程,1个写线程
        new Thread(()->{myList.writeList();},"写线程").start();
        for (int i=0; i<10; i++) {
            new Thread(()->{myList.readList();},"读线程"+(i+1)).start();
        }
    }

}

上記の場合、ReentrantReadWriteLockを使用してCopyOnWriteArrayListを実装しました。メインスレッドは、リストを書き込むための新しい書き込みスレッドと、リストを読み取るための10個のリーダースレッドを作成しました。プログラムの完了には合計2回かかり、11秒かかります。 Vectorを使用します。複数のスレッドの場合、リストの読み取り/書き込みロック操作により、リストの読み取り効率が向上します。リストの読み取り部分では、スレッドが共有されます。リストへの書き込みプロセスでは、書き込みスレッドは次のようになります。同期されているので、結論を導き出すことができます。読み取り/書き込みロックは読み取り-読み取り共有、読み取り-書き込み同期です。

 

 

排他的ロック取得の簡単なプロセス


image.png

上の図に示すように、排他的取得ロックプロセスを単純に整理します。

  1. 排他的ロック取得(上記の例ではWRITE_LOCK書き込みロック)では、最初にスレッドがロックを取得したかどうかを判断し、いずれかのスレッドがロックを取得したかどうかを、読み取り/書き込みロックの32ビットint型状態で取得できます。下位16ビットは読み取りロックを示し、上位16ビットは書き込みロックを意味します。
  2. 読み取りロックあり:直接キューに入れてブロックします。
  3. 書き込みロック:書き込みロックスレッドが自分自身であるかどうかも判断する必要があります。自分自身である場合、ロックは再入可能です。別のスレッドがロックを取得して実行していることを示さない場合、現在のスレッドは次のようになります。キューに入れられ、ブロックされました。
  4. ロックフリー:ロックを直接取得し、他のプリエンプトされた排他ロックスレッドをキューに入れてブロックする必要があります。現在のスレッドの実行が終了したら、ロックを解除し、次のキューに入れられたスレッドにロックを取得するよう通知します。

 

ロックを共有する簡単なプロセス


image.png

上の図に示すように、共有ロックのロック取得プロセスを単純に整理します。

  1. 排他的ロック取得(上記の例ではREAD_LOCK読み取りロック)では、最初にスレッドがロックを取得したかどうかを判別します。
  2. 読み取りロック:現在のスレッドは、この時点で読み取りロックステータスが占有されていることを検出し、スレッドが読み取りロックを取得したことを示しますこのスレッドは、casスピン[無限ループ]を介して読み取りロックを取得します。
  3. 書き込みロック:書き込みロックを保持しているスレッドが自分自身であるかどうかも判断する必要があります。自分自身であり、この時点で読み取りロックが取得されている場合、ロックは正常に取得されます。これをロックダウングレードと呼びます。そうでない場合この時点で取得している他のスレッドがあることを自分で示します。書き込みロックが有効になっている場合は、現在のスレッドをキューに入れてブロックする必要があります。
  4. ロックなし:ロックを直接取得します。

 

書き込みロックの低下


読み取りと書き込みは相互に排他的である言いますが、同じスレッドでは、最初に書き込み、次に読み取りも許可されます。これをロック劣化と呼びます。面接では、共有ロック面接の頻度も比較的高く、理解しやすいので、簡単に説明してみましょう。

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author :jiaolian
 * @date :Created in 2021-01-28 15:44
 * @description:ReentrantReadWriteLock读写锁降级测试
 * @modified By:
 * 公众号:叫练
 */
public class WriteLockLowerTest {

    private static final ReentrantReadWriteLock REENTRANT_READ_WRITE_LOCK = new ReentrantReadWriteLock();
    private static final ReentrantReadWriteLock.WriteLock WRITE_LOCK = REENTRANT_READ_WRITE_LOCK.writeLock();
    private static final ReentrantReadWriteLock.ReadLock READ_LOCK = REENTRANT_READ_WRITE_LOCK.readLock();

    public static void main(String[] args) {
        try {
            WRITE_LOCK.lock();
            System.out.println("获取写锁");
            READ_LOCK.lock();
            System.out.println("获取读锁");
        } finally {
            READ_LOCK.unlock();
            System.out.println("释放写锁");
            WRITE_LOCK.unlock();
            System.out.println("释放读锁");
        }
    }
}

上記のコードのように:プログラムは完了するまで実行でき、ロックが低下する可能性があることを示します。また、上記のプログラムは、最初に読み取りロックを取得し、次に書き込みロックを取得します。プログラムはブロックします。なぜですか。メッセージエリアにコメントを書くために友達を歓迎します!

 

 

総括する


今日は、ReentrantReadWriteLockの読み取り/書き込みロックをわかりやすいテキストで説明しました気に入ったらコメントしてください!注意してください、迷子にならないでください。私はLian [Official Account]と呼ばれ、電話をかけて練習しています。次回お会いできるのを楽しみにしています!

tempimage1611629165941.gif

 

 

おすすめ

転載: blog.csdn.net/duyabc/article/details/113484000