飞道的博客

可重入读写锁介绍:ReentrantReadWriteLock

380人阅读  评论(0)

写在前面:

       学习本文前,建议先了解一下 可重入锁 ReentrantLock ,理解一下可重入是个什么概念

1.概念

       既然已经有了 可重入锁 ReentrantLock,为什么还要有 可重入读写锁 ReentrantReadWriteLock 呢??

       因为 可重入锁 ReentrantLocksynchronized 一样,都是属于独占锁。所谓 独占,即:同一时间只能有一个线程持有锁。ReentrantReadWriteLock 的出现, 就是解决 ReentrantLock 独占锁 带来的性能问题。使用 ReentrantLock 无论是 "写/写"线程、"读/读"线程、"读/写"线程之间的工作都是互斥,同时只能有一个线程进入同步区域。

       然而,在工作的大多场景下,"读/读"线程之间并不会存在互斥的关系,只有"读/写"线程 或 "写/写"线程间的操作才需要互斥。因此,可重入读写锁 ReentrantReadWriteLock 就出现了。重点:ReentrantReadWriteLock 目的就是:提高读操作的吞吐量(可用于读多写少的情况下)。

       ReentrantReadWriteLock 实现的是 ReadWriteLock 接口,而并不是 Lock接口(ReentrantLock实现的是Lock接口)。该接口提供了readLockwriteLock两种锁的操作机制,一个是读锁,一个是写锁。在读写锁 ReadWriteLock中,读锁是共享锁,写锁是独占锁(排它锁)。 此处以两个线程为例来介绍:(分为以下三种情况)

  1. 读/读状态: 两个线程都能获得锁;
  2. 读/写状态: 只有一个线程能够获得锁;
  3. 写/写状态: 有一个线程能够获得锁

2.实例

      现在有两个线程,分别模拟读/读读/写写/写三种情况,来了解两个线程获取锁的情况。此处为了模拟效果,我们会对线程1进行 sleep 休眠操作。(sleep操作会释放当前线程的CPU资源,但是不会释放持有的锁。wait操作才会释放锁,供其他线程使用)

1.(读/读)模式

       如下代码所示,设置线程1 无限制休眠,因为使用的是 ReentrantReadWriteLock读写锁,所以 (读/读)模式 之间不存在互斥关系,此时两个线程都是能够获取到锁的。(本文往下翻会介绍使用 ReentrantLock来实现(读/读)模式,它是存在互斥关系的。而并不是ReadWriteLock 中的读锁是共享锁)

/**
 * TODO 读/读模式
 *
 * @author liuzebiao
 * @Date 2019-12-25 18:33
 */
public class RWLockDemo {

    static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    static Lock read = readWriteLock.readLock(); //读锁

    static Lock write = readWriteLock.writeLock();//写锁

    public static void main(String[] args) {
        //线程1
        new Thread(()->{
            try {
                read.lock();
                System.out.println(Thread.currentThread().getName()+"获得线程");
                TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                read.unlock();
            }
        },"线程1").start();

        //线程2
        new Thread(()->{
            try {
                read.lock();
                System.out.println(Thread.currentThread().getName()+"获得线程");
                for (int i = 0; i < 5; i++) {
                    System.out.println("线程2执行操作");
                }
            } finally {
                read.unlock();
            }
        },"线程2").start();
    }
}

执行结果:

线程1获得线程
线程2获得线程
线程2执行操作
线程2执行操作
线程2执行操作

2.(读/写)模式

       如下代码所示,ReentrantReadWriteLock读写锁中,(读/写)模式 之间是存在互斥关系,此时线程1 处于无限制休眠状态,并不能够释放锁,此时线程2就永远不会获取到锁!!!就更不会执行 System.out 输出操作了

/**
 * TODO 读/写模式
 * 
 * @author liuzebiao
 * @Date 2019-12-25 18:33
 */
public class RWLockDemo {

    static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    static Lock read = readWriteLock.readLock(); //读锁

    static Lock write = readWriteLock.writeLock();//写锁

    public static void main(String[] args) {
        //线程1
        new Thread(()->{
            try {
                write.lock();// 写锁
                System.out.println(Thread.currentThread().getName()+"获得线程");
                TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                write.unlock();
            }
        },"线程1").start();

        //线程2
        new Thread(()->{
            try {
                read.lock();//读锁
                System.out.println(Thread.currentThread().getName()+"获得线程");
                for (int i = 0; i < 5; i++) {
                    System.out.println("线程2执行操作");
                }
            } finally {
                read.unlock();
            }
        },"线程2").start();
    }
}

执行结果:

线程1获得线程

3.(写/写)模式

       如下代码所示,ReentrantReadWriteLock读写锁中,(写/写)模式 之间也是存在互斥关系,此时线程1 处于无限制休眠状态,并不能够释放锁,此时线程2也是永远不会获取到锁!!!就更不会执行 System.out 输出操作了

/**
 * TODO 写/写模式
 *
 * @author liuzebiao
 * @Date 2019-12-25 18:33
 */
public class RWLockDemo {

    static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    static Lock read = readWriteLock.readLock(); //读锁

    static Lock write = readWriteLock.writeLock();//写锁

    public static void main(String[] args) {
        //线程1
        new Thread(()->{
            try {
                write.lock();// 写锁
                System.out.println(Thread.currentThread().getName()+"获得线程");
                TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                write.unlock();
            }
        },"线程1").start();

        //线程2
        new Thread(()->{
            try {
                read.lock();//读锁
                System.out.println(Thread.currentThread().getName()+"获得线程");
                for (int i = 0; i < 5; i++) {
                    System.out.println("线程2执行操作");
                }
            } finally {
                read.unlock();
            }
        },"线程2").start();
    }
}

执行结果:

线程1获得线程

3.使用 ReentrantLock 模拟读/读操作

/**
 * TODO ReentrantLock 模拟 读/读操作
 *
 * @author liuzebiao
 * @Date 2019-12-25 18:33
 */
public class RWLockDemo {

//    static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    static ReentrantLock lock = new ReentrantLock();

//    static Lock read = readWriteLock.readLock(); //读锁

//    static Lock write = readWriteLock.writeLock();//写锁

    public static void main(String[] args) {
        //线程1
        new Thread(()->{
            try {
                lock.lock();
                System.out.println(Thread.currentThread().getName()+"获得线程");
                TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        },"线程1").start();

        //线程2
        new Thread(()->{
            try {
                lock.lock();
                System.out.println(Thread.currentThread().getName()+"获得线程");
                for (int i = 0; i < 5; i++) {
                    System.out.println("线程2执行操作");
                }
            } finally {
                lock.unlock();
            }
        },"线程2").start();
    }
}

执行结果:

线程1获得线程

4.总结:

       使用 ReentrantLock 方式来处理 读/读模式,通过结果证明它是独占锁。而在上文中使用 ReentrantReadWriteLock 示例中,读锁是共享锁,在处理读/读模式时,每个线程都是可以获得锁的。这也就印证了开篇一句话:。重点:ReentrantReadWriteLock 目的就是:提高读操作的吞吐量。


可重入读写锁 ReentrantReadWriteLock,介绍到此为止

如果本文对你有所帮助,那就给我点个赞呗

End


转载:https://blog.csdn.net/lzb348110175/article/details/105510930
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场