• 面试突击45:为什么要用读写锁?它有什么优点?


    读写锁(Readers-Writer Lock)顾名思义是一把锁分为两部分:读锁和写锁,其中读锁允许多个线程同时获得,因为读操作本身是线程安全的,而写锁则是互斥锁,不允许多个线程同时获得写锁,并且写操作和读操作也是互斥的。总结来说,读写锁的特点是:读读不互斥、读写互斥、写写互斥

    1.读写锁使用

    在 Java 语言中,读写锁是使用 ReentrantReadWriteLock 类来实现的,其中:

    • ReentrantReadWriteLock.ReadLock 表示读锁,它提供了 lock 方法进行加锁、unlock 方法进行解锁。
    • ReentrantReadWriteLock.WriteLock 表示写锁,它提供了 lock 方法进行加锁、unlock 方法进行解锁。

    它的基础使用如下代码所示:

    // 创建读写锁
    final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    // 获得读锁
    final ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
    // 获得写锁
    final ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
    // 读锁使用
    readLock.lock();
    try {
        // 业务代码...
    } finally {
        readLock.unlock();
    }
    // 写锁使用
    writeLock.lock();
    try {
        // 业务代码...
    } finally {
        writeLock.unlock();
    }
    

    1.1 读读不互斥

    多个线程可以同时获取到读锁,称之为读读不互斥,如下代码所示:

    // 创建读写锁
    final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    // 创建读锁
    final ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
    Thread t1 = new Thread(() -> {
        readLock.lock();
        try {
            System.out.println("[t1]得到读锁.");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("[t1]释放读锁.");
            readLock.unlock();
        }
    });
    t1.start();
    Thread t2 = new Thread(() -> {
        readLock.lock();
        try {
            System.out.println("[t2]得到读锁.");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("[t2]释放读锁.");
            readLock.unlock();
        }
    });
    t2.start();
    

    以上程序执行结果如下:
    image.png

    1.2 读写互斥

    读锁和写锁同时使用是互斥的(也就是不能同时获得),这称之为读写互斥,如下代码所示:

    // 创建读写锁
    final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    // 创建读锁
    final ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
    // 创建写锁
    final ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
    // 使用读锁
    Thread t1 = new Thread(() -> {
        readLock.lock();
        try {
            System.out.println("[t1]得到读锁.");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("[t1]释放读锁.");
            readLock.unlock();
        }
    });
    t1.start();
    // 使用写锁
    Thread t2 = new Thread(() -> {
        writeLock.lock();
        try {
            System.out.println("[t2]得到写锁.");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("[t2]释放写锁.");
            writeLock.unlock();
        }
    });
    t2.start();
    

    以上程序执行结果如下:
    image.png

    1.3 写写互斥

    多个线程同时使用写锁也是互斥的,这称之为写写互斥,如下代码所示:

    // 创建读写锁
    final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    // 创建写锁
    final ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
    Thread t1 = new Thread(() -> {
        writeLock.lock();
        try {
            System.out.println("[t1]得到写锁.");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("[t1]释放写锁.");
            writeLock.unlock();
        }
    });
    t1.start();
    
    Thread t2 = new Thread(() -> {
        writeLock.lock();
        try {
            System.out.println("[t2]得到写锁.");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("[t2]释放写锁.");
            writeLock.unlock();
        }
    });
    t2.start();
    

    以上程序执行结果如下:
    image.png

    2.优点分析

    1. 提高了程序执行性能:多个读锁可以同时执行,相比于普通锁在任何情况下都要排队执行来说,读写锁提高了程序的执行性能。

    2. 避免读到临时数据:读锁和写锁是互斥排队执行的,这样可以保证了读取操作不会读到写了一半的临时数据。

    3.适用场景

    读写锁适合多读少写的业务场景,此时读写锁的优势最大。

    总结

    读写锁是一把锁分为两部分:读锁和写锁,其中读锁允许多个线程同时获得,而写锁则是互斥锁。它的完整规则是:读读不互斥、读写互斥、写写互斥。它适用于多读的业务场景,使用它可以有效的提高程序的执行性能,也能避免读取到操作了一半的临时数据。

    是非审之于己,毁誉听之于人,得失安之于数。

    公众号:Java面试真题解析

    面试合集:https://gitee.com/mydb/interview

  • 相关阅读:
    仿牛客网项目第四章:Redis,一站式高性能存储方案(详细步骤和思路)
    ROS2与turtlebot4仿真入门教程-安装ROS2
    node实现basic认证 及fs模块读取html,进行路由跳转
    小红书笔记详情API:挖掘小红书社区的秘密宝藏
    前端性能优化方法与实战02 性能瓶颈点:从 URL 输入到页面加载整过程分析
    详解联邦学习中的异构模型集成与协同训练技术
    基于 ECDSA 的 BSV 预言机
    计算机操作系统学习(六)设备管理
    vue中全局修改elementui,message修改时长
    Mysql日志
  • 原文地址:https://www.cnblogs.com/vipstone/p/16241070.html