6、ReentrantReadWriteLock

前言

相比于ReentrantLock 互斥的设计,现实情况是我们更多的碰到的是 读的次数远远大于写的次数。如果在一个读场景远大于写场景的情况下,我们再去使用ReentrantLock 显得浪费资源。这里介绍ReentrantReadWriteLock(读写锁) 就是为了解决这个问题。

1、ReentrantReadWriteLock 用法

ReentrantReadWriteLock 分为读锁和写锁。

所谓的读锁 其实是就是AQS 中的共享模式,允许多个线程同时持有共享锁。而写锁则是AQS 中的独占模式,只有一个线程能够获取锁。
前面文章介绍过AQS ,对了共享和独占模式应该有所了解。

那么这里有一个问题,我们之前说过AQS 中维护了一个state,但是在ReentrantReadWriteLock如何同时保存写锁和读锁的状态的?
答案是 ReentrantReadWriteLock 将state 分成了 高16位 和 低16位。高16代表的是读锁的状态,所以共享锁的线程不会超过 2 << 16,
低16位是独占锁的状态。

(1)读锁和写锁的获取条件

1、写锁 同ReentrantLock 。无其他线程 持有写锁,没有线程持有读锁
2、读锁 没有其他线程持有写锁并且持有读锁的线程不超过 (2<<16) - 1 原因见上面

(2)读写锁的锁降级

因为写锁是独占锁,相比于读锁永远更高的权限。我们认为从写锁到读锁是一种锁的降级。什么情况会发生锁得降级呢,我们来分析一下。
1、读锁要想获取写锁是不可能的,因为写锁是和一切读锁互斥(包括自己拥有的)。
2、写锁可以获取读锁,见上面写锁获取条件

综上,根据读锁和写锁获取的条件 读写锁只能降级 无法升级。下面实验一下

锁升级

 public static void main(String[] args) {

        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
      
        lock.readLock().lock();
        System.out.println("小米:我这有一个读锁");

        lock.writeLock().lock();
        System.out.println("小米:我从读锁中拿到一个读锁");

        lock.writeLock().unlock();
        System.out.println("小米:写锁解开吧!!!");

        lock.readLock().unlock();
        System.out.println("小米:我也不要读锁了。给你吧");
    }

输出结果


image.png

可见在拥有了读锁的情况下,线程时拿不到写锁的。

下面我们读写锁互换位置试试

锁降级

    public static void main(String[] args) {

        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        lock.writeLock().lock();
        System.out.println("小米:我从读锁中拿到一个读锁");

        lock.readLock().lock();
        System.out.println("小米:我这有一个读锁");


        lock.writeLock().unlock();
        System.out.println("小米:写锁解开吧!!!");

        lock.readLock().unlock();
        System.out.println("小米:我也不要读锁了。给你吧");

    }

输出结果


.

从输出结果来看,先获取写锁再获取读锁是没有问题的。

再看看一个正常使用的例子

public static void main(String[] args) {

        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

        new Thread(() -> {
            lock.readLock().lock();
            System.out.println("小米:我这有一个读锁");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("时间过去了三秒");
            System.out.println("小米:我不要读锁了。给你吧");
            lock.readLock().unlock();
        }).start();

        new Thread(() -> {
            System.out.println("小明:我想要一个写锁");
            lock.writeLock().lock();
            System.out.println("小明:嘻嘻,我获取了一个写锁");
            lock.writeLock().unlock();
        }).start();
    }

一个线程持有读锁三秒,一个线程等待读锁
输出结果


image.png

到这里ReentrantReadWriteLock 已经结束了,还有什么疑问吗?

你可能感兴趣的:(6、ReentrantReadWriteLock)