• ReadWriteLock锁的应用


    对于 Lock 锁来说,如果要实现 “一写多读” 的并发状态(即允许同时读,不允许同时写),需要对 “写操作” 加锁,对 “读操作” 不作要求即可。但是如果对于 “读” 操作下,有 “写操作” 接入的话,对于当前的 “读操作” 可能会产生 “幻读” 的现象。所以对于要实现 “一写多读” 的情况下,应推荐使用 ReadWriteLock 锁。

    ReadWriteLock 是与 Lock 平级的一个 JUC 包下的接口

    它唯一的实现类是 ReentrantReadWriteLock 类,一个 ReentrantReadWriteLock 对象维护了一对关联的locks ,一个用于只读操作,一个用于写入。

    简单来说:

    • 它维护了两个锁,一个叫 “读锁”,一个叫 “写锁”。
    • 为了允许 “一写多读” 的操作,按理上,“写锁” 一旦上锁,不能再被获取;而为了保证能同时读数据,“读锁” 若上锁,想获取 “读锁” 的线程仍然可以执行读操作,而为了防止 “读操作” 执行时有 “写操作” 的接入,应该要防止 “写锁” 被获取。

    下面通过 4 个不同顺序的读写实例来演示一遍(代码贴在最后面)

    1、一个线程已获取 “写锁” 状态下,另一个线程尝试获取 “读锁” 。





    在 “读锁” 已被获取的情况下,“写锁” 不能被获取。(即便是在多个线程都获取 “读锁”,“写锁” 必须在所有 “读操作” 结束后才能被获取,可自行测试)

    2、一个线程已获取 “读锁” 状态下,另一个线程尝试获取 “写锁” 。





    在 “写锁” 已被获取的情况下,“读锁” 不能被获取。

    3、一个线程已获取 “读锁” 状态下,另一个线程尝试获取 “读锁” 。





    在 “读锁” 已被获取的情况下,“读锁” 还能再被获取,类似于 Semaphore 辅助类。

    4、一个线程已获取 “写锁” 状态下,另一个线程尝试获取 “写锁” 。





    在 “写锁” 已被获取的情况下,“写锁” 不能再被获取。


    总代码

    package ReadWriteLock;
    
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    public class ReadWriteLockDemo {
    
        private final static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    
        public static void main(String[] args) throws InterruptedException {
            writeAndWriteTest();
        }
    
        public static void readAndWriteTest() throws InterruptedException {
            //第一个线程先获取写锁,另一个线程再获取读锁
            new Thread(() -> {
                readWriteLock.writeLock().lock();
                System.out.println("写锁已获取");
                try {
                    Thread.sleep(8000);
                    readWriteLock.writeLock().unlock();
                    System.out.println("已关闭写锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
            Thread.sleep(3000);
            System.out.println("另一个线程尝试获取读锁");
            new Thread(() -> {
                readWriteLock.readLock().lock();
                System.out.println("另一个线程的读锁已获取");
            }).start();
        }
    
        public static void writeAndReadTest() throws InterruptedException {
            //第一个线程先获取读锁,另一个线程再获取写锁
            new Thread(() -> {
                readWriteLock.readLock().lock();
                System.out.println("读锁已获取");
                try {
                    Thread.sleep(8000);
                    readWriteLock.readLock().unlock();
                    System.out.println("已关闭读锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
            Thread.sleep(3000);
            System.out.println("另一个线程尝试获取写锁");
            new Thread(() -> {
                readWriteLock.writeLock().lock();
                System.out.println("另一个线程的写锁已获取");
            }).start();
        }
    
        public static void readAndReadTest() throws InterruptedException {
            //一个线程已获读锁,另一个线程再获取读锁
            new Thread(() -> {
                readWriteLock.readLock().lock();
                System.out.println("读锁已获取");
                try {
                    Thread.sleep(8000);
                    readWriteLock.readLock().unlock();
                    System.out.println("已关闭读锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
            Thread.sleep(3000);
            System.out.println("另一个线程尝试获取读锁");
            new Thread(() -> {
                readWriteLock.readLock().lock();
                System.out.println("另一个线程的读锁已获取");
            }).start();
        }
    
        public static void writeAndWriteTest() throws InterruptedException {
            //一个线程已获写锁,另一个线程再获取写锁
            new Thread(() -> {
                readWriteLock.writeLock().lock();
                System.out.println("写锁已获取");
                try {
                    Thread.sleep(8000);
                    readWriteLock.writeLock().unlock();
                    System.out.println("已关闭写锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
            Thread.sleep(3000);
            System.out.println("另一个线程尝试获取写锁");
            new Thread(() -> {
                readWriteLock.writeLock().lock();
                System.out.println("另一个线程的写锁已获取");
            }).start();
        }
    }
    

    总结

    ReentrantReadWriteLock 中的 “写锁” 类似于 Lock 锁,而 “读锁” 类似于 Semaphore 信号量机制。“写锁” 保证临界资源能被唯一占用,解决了 “写写同步问题”。而当且仅当 “读锁” 队列为空时,“写锁” 才能被获取。且 “写锁” 和 “读锁” 在同一时刻不能同时被持有,解决了 “读写同步问题”,且它还能保证能够 “同时读” 的特性。

  • 相关阅读:
    CentOS 基本设置
    CentOS 7 编译安装 Code::Blocks
    Java和C/C++进行DES/AES密文传输(借鉴)
    VS 2010 编译 Openssl
    Crypto++ 动态链接编译与实例测试
    c++11-bind的用法
    模板编程-迭代器
    LeetCode-Swap Nodes in Pairs
    LeetCode-Generate Parentheses
    Leetcode-Letter Combinations of a Phone Number
  • 原文地址:https://www.cnblogs.com/Absofuckinglutely/p/13280042.html
Copyright © 2020-2023  润新知