共享锁

简介: 共享锁允许多个线程同时读取共享资源,写操作时阻塞其他线程,通过“读共享、写独占”策略提升并发性能,适用于读多写少场景,如缓存、数据库查询等。

共享锁(Shared Lock)是一种并发控制机制,允许多个线程同时读取共享资源,但在写操作时会阻塞其他线程的读写请求。它通过“读共享、写独占”的策略,在保证数据一致性的同时提升了读操作的并发性能。

核心原理

  1. 读写分离策略

    • 读锁(共享锁):允许多个线程同时获取,用于读取操作。
    • 写锁(排他锁):同一时间仅允许一个线程获取,用于写入操作。
  2. 锁兼容性

    • 读锁之间:相互兼容,可同时持有。
    • 读锁与写锁:互斥,写锁需等待所有读锁释放。
    • 写锁之间:互斥,同一时间仅一个写锁。

共享锁的优缺点

  • 优点

    • 读并发提升:适合读多写少场景,如缓存系统、数据库查询。
    • 数据一致性:写操作时独占资源,避免脏写。
  • 缺点

    • 写饥饿风险:若读请求频繁,写操作可能长时间等待。
    • 实现复杂度高:需维护锁状态和等待队列。

实现方式

1. 数据库层面

  • MySQL
    SELECT * FROM table WHERE id = ? LOCK IN SHARE MODE; -- 共享读锁
    
    • 其他事务可读取该行,但无法修改,直至当前事务提交。

2. Java语言实现

  • ReentrantReadWriteLock

    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    public class ReadWriteLockExample {
         
        private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
        private int data = 0;
    
        public int readData() {
         
            rwLock.readLock().lock(); // 获取读锁
            try {
         
                return data;
            } finally {
         
                rwLock.readLock().unlock(); // 释放读锁
            }
        }
    
        public void writeData(int newValue) {
         
            rwLock.writeLock().lock(); // 获取写锁
            try {
         
                data = newValue;
            } finally {
         
                rwLock.writeLock().unlock(); // 释放写锁
            }
        }
    }
    

共享锁的典型应用场景

  1. 缓存系统

    • 读操作获取共享锁,写操作(如缓存更新)获取排他锁。
  2. 数据库查询

    • 多事务并发读取同一数据时,使用共享锁避免阻塞。
  3. 配置管理

    • 频繁读取、偶尔修改的配置文件。

与其他锁的对比

锁类型 读并发 写并发 互斥对象 适用场景
共享锁 允许多个 独占 写锁 读多写少,如数据库查询
排他锁 独占 独占 读锁、写锁 写操作为主,如文件写入
乐观锁 无锁 CAS检查 无(冲突重试) 冲突少的读多写少场景
悲观锁 独占 独占 所有操作 冲突多的写操作场景

注意事项

  1. 写饥饿问题

    • 高并发读场景下,写锁可能长时间等待。
    • 解决方案:使用公平锁(如ReentrantReadWriteLock(true))。
  2. 锁降级

    • 写锁可降级为读锁,但读锁不能升级为写锁(可能导致死锁)。
    public void upgradeLockExample() {
         
        rwLock.writeLock().lock();
        try {
         
            // 写操作
            rwLock.readLock().lock(); // 降级:先获取读锁
            rwLock.writeLock().unlock(); // 再释放写锁
            // 继续读操作
        } finally {
         
            rwLock.readLock().unlock();
        }
    }
    
  3. 性能权衡

    • 锁状态管理开销较大,需根据业务特点选择是否使用。

Java中的共享锁工具

  1. ReentrantReadWriteLock

    • 可重入的读写锁实现,支持公平/非公平模式。
  2. StampedLock(JDK 8+)

    • 支持乐观读锁,在读多写少场景下性能更优。
    import java.util.concurrent.locks.StampedLock;
    
    public class StampedLockExample {
         
        private final StampedLock lock = new StampedLock();
        private int data = 0;
    
        public int optimisticRead() {
         
            long stamp = lock.tryOptimisticRead(); // 获取乐观读戳记
            int currentData = data;
            if (!lock.validate(stamp)) {
          // 检查期间是否有写操作
                stamp = lock.readLock(); // 升级为悲观读锁
                try {
         
                    currentData = data;
                } finally {
         
                    lock.unlockRead(stamp);
                }
            }
            return currentData;
        }
    }
    

总结

共享锁通过“读共享、写独占”的策略,在保证数据一致性的前提下提升了读操作的并发性能,尤其适合读多写少的场景。但需注意写饥饿问题和锁升级/降级的正确使用。在高并发系统中,合理使用共享锁可显著优化吞吐量,但需根据业务特点进行性能测试和调优。

目录
相关文章
|
2月前
|
存储 Java 对象存储
轻量级锁
轻量级锁是JVM为提升多线程性能而引入的锁机制,通过CAS操作减少线程阻塞,适用于同步块执行时间短且线程竞争不激烈的场景。其核心在于使用栈帧中的锁记录与CAS操作实现高效加锁,避免用户态与内核态切换带来的性能损耗。当无竞争时,仅需一次CAS即可完成锁获取;若竞争激烈,则可能升级为重量级锁。相比偏向锁和重量级锁,轻量级锁在低竞争环境下具有更高的效率。
77 0
|
2月前
|
存储 安全 Java
synchronized 锁升级
JDK 6 引入的 synchronized 锁升级机制,通过偏向锁、轻量级锁和重量级锁的动态切换,优化了多线程同步性能。该机制根据竞争情况逐步升级锁状态,减少线程阻塞和系统调用开销,从而提升并发效率。
123 0
|
2月前
|
Java Spring
聊聊你对SpringBoot框架的理解 ?
SpringBoot是Spring家族中流行的子项目,旨在简化Spring框架开发的繁琐配置。它主要提供三大功能:starter起步依赖简化依赖管理,自动配置根据条件创建Bean,以及内嵌Web服务器支持Jar包运行,极大提升了开发效率。
125 0
|
2月前
|
缓存 Java
对比 synchronized 和 volatile
`synchronized` 和 `volatile` 是 Java 并发编程中的两个关键机制,各有侧重。`synchronized` 用于实现线程的互斥访问,保证原子性、可见性和有序性,适用于需要锁的场景;而 `volatile` 更轻量,仅确保变量的可见性和有序性,适用于状态标志等无需复合操作的场景。两者可互补使用,如双重检查单例中结合二者优势。合理选择有助于提升并发性能与代码安全性。
120 0
|
2月前
|
Java Spring
Spring Boot配置的优先级?
在Spring Boot项目中,配置可通过配置文件和外部配置实现。支持的配置文件包括application.properties、application.yml和application.yaml,优先级依次降低。外部配置常用方式有Java系统属性(如-Dserver.port=9001)和命令行参数(如--server.port=10010),其中命令行参数优先级高于系统属性。整体优先级顺序为:命令行参数 > Java系统属性 > application.properties > application.yml > application.yaml。
528 0
|
2月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
729 0
|
2月前
|
监控 Java 关系型数据库
排他锁
排他锁(写锁)是一种互斥机制,确保同一时间仅一个线程访问共享资源,保障数据一致性与完整性。适用于写操作场景,如更新、删除等,常见于数据库和多线程编程。其优点为强一致性和实现简单,但并发度低且存在死锁风险。可通过synchronized、ReentrantLock等方式实现。
66 0
|
2月前
|
存储 Java 容器
偏向锁
偏向锁是JVM为提升单线程环境下锁性能而引入的优化机制。其核心思想是将锁偏向首个获取它的线程,避免无竞争时的同步开销,从而提高执行效率。适用于锁由同一线程多次获取、无并发竞争的场景。一旦出现多线程竞争,偏向锁会撤销并升级为更高级别的锁。合理使用可显著提升性能,但在高并发环境下需谨慎配置。
46 0
|
2月前
|
Java Maven 开发工具
SpringBoot使用汇总
本节介绍 Spring Boot 工程的构建方法,包括使用 IDEA 快速创建项目、通过官方平台生成项目、配置 Maven 以及设置编码格式等内容。涵盖 Group 和 Artifact 的填写规范、依赖添加、IDE 配置与推荐设置,助力快速搭建开发环境。
117 3
|
2月前
|
Java 应用服务中间件 Maven
SpringBoot使用汇总
本节介绍了Spring Boot项目工程结构,包含src/main/java(业务代码)、src/main/resources(静态与配置文件)和src/test/java(测试代码)。通过@SpringBootApplication注解的启动类运行main方法即可快速启动应用。Spring Boot内置Tomcat,简化配置流程。示例展示了创建Controller、访问接口及修改默认端口的方法,帮助开发者快速上手Spring Boot开发。
100 2

热门文章

最新文章