读写锁分离设计模式详解

简介: 大家好,我是V哥。在商城系统中,用户浏览商品详情页时可以查看库存数量,这是高频的读操作;当用户下单成功时,系统会更新库存数量,这是低频的写操作。为了提升系统的并发性和性能,读写锁分离设计模式是最好的选择。该模式通过将读操作和写操作分离,允许多个读线程并发执行,而写操作则需要独占锁,确保数据一致性。适用于读多写少的场景,如库存管理、缓存读取等。通过读写锁分离,可以显著提升系统的性能和用户体验。

大家好,我是 V 哥。商城系统中,用户在浏览商品详情页时可以查看库存数量,这是读操作,频率较高。当用户下单成功时,系统会更新库存数量,这是写操作,但相对较少。这是一个再常见不过的应用场景了,在这种场景下,读写锁分离设计模式就是最好的武器。

读写锁分离设计模式是一种多线程设计模式,适合在有读多写少的场景中使用。它通过读写操作的分离,提升了系统的并发性和性能。在这个模式中,读操作是共享的,可以同时被多个线程执行,而写操作需要独占锁,避免并发写入带来的数据不一致问题。

读写锁分离设计模式的原理

咱们再来把读写锁分离的原理先明确一下:

  1. 读写锁(ReadWriteLock)

    • 读写锁提供了两种锁:读锁和写锁。
    • 读锁是共享的,可以允许多个线程同时持有,多个线程可以并发读取数据。
    • 写锁是独占的,只有一个线程能获取写锁。写操作会阻塞所有的读写操作,确保数据一致性。
  2. 适用场景

    • 读操作远多于写操作,数据写入不频繁的场景,如缓存、配置读取、数据分析等。
    • 通过读写分离,可以避免读操作阻塞,从而提高系统的吞吐量和并发性。

特别的爱给特别的你,满足才是硬道理

咱们就拿在电商平台的商品库存管理系统来说,库存数据需要满足如下业务需求:

  1. 高并发访问:商城有大量用户会同时访问商品详情页,查询库存数量。访问这些商品库存的用户数是巨大的,因此读取库存的操作需要具备高并发能力,保证每个用户能够快速查询到最新的库存信息。

  2. 实时更新库存:当用户成功下单,库存需要相应减少。此外,可能会有后台系统进行库存更新操作,比如在补货时增加库存。因此,写操作虽然较少,但必须做到线程安全,确保更新数据的准确性,避免因并发写入导致库存错误。

  3. 数据一致性要求:为了确保库存数据的一致性,写操作必须是独占的,也就是说,只有在没有任何读或写操作时,系统才能进行写操作,从而避免多个线程同时写入导致的库存数据错误。同时,也要保证在进行写操作时,读线程不能获取到库存的中间状态,确保用户获取的是准确的库存信息。

  4. 读多写少:在实际业务中,库存查询的频率远高于更新库存的频率,绝大部分用户操作仅涉及查询商品是否有库存,而少部分用户操作涉及到更新库存,比如完成下单、取消订单、或者后台管理员进行库存调整。

  5. 性能要求:库存查询的响应速度直接影响到用户体验,特别是在大型促销活动中,大量用户同时访问某些热门商品的库存数据,因此必须保证高并发读取的性能。而写操作因为较少,不会频繁发生,能容忍一定的等待时间。

具体操作场景

回到功能业务,通常要实现的具体功能场景是这样的:

  • 库存查询:用户在浏览商品详情页时,都会查看商品库存。一个页面可能会展示多个商品的库存,因此多个用户并发查询不同商品的库存时,系统需要支持多个读线程同时读取数据,而不会互相干扰。

  • 库存更新:当用户下单购买商品时,系统需要减少相应的库存数量。这个写操作必须是独占的,以避免并发写入导致库存数量的不一致。更新操作还会在订单取消、订单失效等情况下发生。此外,后台的库存补货也是一种写操作,更新后的库存应能被用户实时查询到。

  • 促销场景:在大促活动中(如双11、黑五促销),某些商品会有大量用户访问库存。如果不进行读写锁分离,频繁的写锁会阻塞所有的读线程,导致用户体验下降。因此,系统应支持多个用户同时进行读取操作,同时保障写入的独占性,以平衡高并发和数据一致性的需求。

实施目标

有了这样的场景,采用读写锁分离设计模式来优化库存管理,可以达到以下目标:

  1. 提升读操作的并发性:允许多个用户并发查询库存,保证在读多写少的场景下库存查询的高效性。
  2. 保证写操作的独占性:避免并发写入的冲突,确保库存数据的一致性,满足商品库存管理系统的数据准确性需求。
  3. 降低读写冲突的等待时间:在写操作较少的情况下,通过读写锁分离有效降低读操作的等待时间,提升系统整体性能。

案例:商品库存管理

下面咱们来具体看一下案例实现,我们要实现一个商品库存管理,需求是这样滴:

  • 多个线程可以同时读取某商品的库存数量。
  • 只有一个线程可以更新库存,避免多个写操作造成数据不一致。

一、实现步骤

  1. 定义商品库存管理类 InventoryManager,使用 ReentrantReadWriteLock 进行读写锁分离。
  2. 创建 checkStock 方法进行库存读取操作,获取读锁。
  3. 创建 updateStock 方法进行库存更新操作,获取写锁。

以下是实现代码:

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class InventoryManager {
   
    // 商品库存存储
    private final Map<String, Integer> inventory = new HashMap<>();
    // 读写锁
    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
    private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();

    // 获取库存数量(读操作)
    public int checkStock(String productId) {
   
        readLock.lock();
        try {
   
            return inventory.getOrDefault(productId, 0);
        } finally {
   
            readLock.unlock();
        }
    }

    // 更新库存数量(写操作)
    public void updateStock(String productId, int quantity) {
   
        writeLock.lock();
        try {
   
            int currentStock = inventory.getOrDefault(productId, 0);
            inventory.put(productId, currentStock + quantity);
            System.out.println("Updated stock for product " + productId + ": " + (currentStock + quantity));
        } finally {
   
            writeLock.unlock();
        }
    }
}

二、测试案例

在测试案例中,模拟多个用户并发访问库存,读取库存的线程可以并发执行,而更新库存的线程会独占锁。

public class InventoryManagerTest {
   
    public static void main(String[] args) {
   
        InventoryManager inventoryManager = new InventoryManager();

        // 初始化库存
        inventoryManager.updateStock("product_1", 100);

        // 模拟多个线程同时读取库存
        for (int i = 0; i < 5; i++) {
   
            new Thread(() -> {
   
                System.out.println("Stock for product_1: " + inventoryManager.checkStock("product_1"));
            }).start();
        }

        // 模拟一个线程更新库存
        new Thread(() -> {
   
            inventoryManager.updateStock("product_1", -10);
            System.out.println("Stock after selling 10 units for product_1: " + inventoryManager.checkStock("product_1"));
        }).start();
    }
}

三、代码分析

  • 读操作 (checkStock):

    • 使用 readLock 加锁,只需获取读锁,不会影响其他读取线程。
    • 多个读取线程可以同时进入 checkStock 方法,提升读取并发性。
  • 写操作 (updateStock):

    • 使用 writeLock 加锁,写操作会阻塞其他读写操作。
    • 确保写入操作是独占的,防止并发写操作导致的数据不一致问题。

四、运行结果示例

输出可能如下(顺序可能有所不同):

Stock for product_1: 100
Stock for product_1: 100
Stock for product_1: 100
Stock for product_1: 100
Stock for product_1: 100
Updated stock for product product_1: 90
Stock after selling 10 units for product_1: 90

五、优缺点

  • 优点:读写锁分离允许多个读线程并发执行,大大提高了读操作的效率,适合读多写少的场景。
  • 缺点:如果写操作频繁,写锁会阻塞读操作,可能会降低系统性能,但在写安全重要程度来看,牺牲点性能是完全可以忍受的。

到这里,你是不是可以感受到读写锁分离设计模式解决了大问题了呢,并发场景下我们必须要考虑这个问题。你学沸了吗,关注威哥爱编程,一起搞不寂寞。

相关文章
|
设计模式 Java 容器
一篇神文就把java多线程,锁,JMM,JUC和高并发设计模式讲明白了
今天给大家分享一篇一线开发大牛整理的java高并发核心编程神仙文档,里面主要包含的知识点有:多线程、线程池、内置锁、JMM、CAS、JUC、高并发设计模式、Java异步回调、CompletableFuture类等。
|
设计模式 安全 算法
Java多线程(二)、线程的生命周期、线程的同步、Synchronized的使用方法、同步代码块、同步方法、同步机制中的锁、同步的范围、Lock(锁、不会释放锁的操作、单例设计模式之懒汉式(线程安全)
Java多线程(二)、线程的生命周期、线程的同步、Synchronized的使用方法、同步代码块、同步方法、同步机制中的锁、同步的范围、Lock(锁、不会释放锁的操作、单例设计模式之懒汉式(线程安全)
Java多线程(二)、线程的生命周期、线程的同步、Synchronized的使用方法、同步代码块、同步方法、同步机制中的锁、同步的范围、Lock(锁、不会释放锁的操作、单例设计模式之懒汉式(线程安全)
|
安全 Java 设计模式
JAVA之旅(十四)——静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制
JAVA之旅(十四)——静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制 一.静态同步函数的锁是class对象 我们在上节验证了同步函数的锁是this,但是对于静态同步函数,你又知道多少呢? 我们做一个这样的小实验,我们给show.
1428 0
|
安全 Java 设计模式
JAVA之旅(十四)——静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制
JAVA之旅(十四)——静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制 JAVA之旅,一路有你,加油! 一.
809 0
|
19天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
2月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
21天前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
14天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
36 1
|
1月前
|
设计模式 Java Kotlin
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
本教程详细讲解Kotlin语法,适合希望深入了解Kotlin的开发者。对于快速学习Kotlin语法,推荐查看“简洁”系列教程。本文重点介绍了构建者模式在Kotlin中的应用与改良,包括如何使用具名可选参数简化复杂对象的创建过程,以及如何在初始化代码块中对参数进行约束和校验。
21 3
|
2月前
|
设计模式 算法 安全
设计模式——模板模式
模板方法模式、钩子方法、Spring源码AbstractApplicationContext类用到的模板方法
设计模式——模板模式