Java并发编程:乐观锁、悲观锁、公平锁、非公平锁

在Java并发编程中,理解不同类型的锁及其适用场景至关重要。下面这个表格汇总了乐观锁、悲观锁、公平锁和非公平锁的核心特性和典型实现,希望能帮助你快速建立整体概念。

锁类型 核心思想/假设 典型实现举例
​乐观锁​ 假设并发冲突概率很小,先操作数据,提交时再检测冲突。 1. ​版本号/时间戳机制 ​ (常用于数据库) 2. ​原子类 ​ (java.util.concurrent.atomic包下的 AtomicInteger等,基于CAS)
​悲观锁​ 假设并发冲突概率很高,访问数据前先加锁,确保操作期间的排他性。 1. ​synchronized关键字 ​ 2. ​ReentrantLock等 Lock接口实现类 ​ 3. ​数据库悲观锁 ​ (如 SELECT ... FOR UPDATE)
​公平锁​ 按照线程请求锁的先后顺序来分配锁,保证先到先得,避免线程"饥饿"。 1. ​ReentrantLock(true) ​ (构造时传入 true) 2. ​Semaphore等并发工具也可配置为公平模式​
​非公平锁​ 允许"插队",线程获取锁的顺序不一定与请求顺序一致,可能提高吞吐量。 1. ​synchronized关键字​ (内置实现即为非公平锁) 2. ​ReentrantLock()/ ReentrantLock(false) ​ (默认或显式设置为非公平)

💡 项目实战与选择策略

了解概念和实现后,关键在于如何在实战中做出正确选择。

🔐 乐观锁 vs 悲观锁

选择取决于并发冲突的概率 和应用场景。

  • ​乐观锁适用场景 ​:​读多写少,冲突概率低的场景,如:

    • 商品信息更新:多个管理员可能同时修改商品描述,但冲突概率不高。
    • 用户积分微调:并发修改次数相对较少。
    • 数据库并发更新:通过版本号字段控制。
    ini 复制代码
    // 示例:基于版本号的乐观锁数据库更新模式
    String sql = "UPDATE products SET name = ?, price = ?, version = version + 1 WHERE id = ? AND version = ?";
    int affectedRows = jdbcTemplate.update(sql, newName, newPrice, productId, currentVersion);
    if (affectedRows == 0) {
        // 处理更新失败(版本号不匹配),通常重试或抛出异常
        throw new OptimisticLockingFailureException("数据已被其他事务修改");
    }
  • ​悲观锁适用场景 ​:​写多读少 ,冲突概率高,或需要强一致性的场景,如:

    • 银行转账:涉及关键资金,必须保证绝对准确。
    • 库存扣减/秒杀:高并发下防止超卖。
    • 数据库事务中锁定关键记录。
    csharp 复制代码
    // 示例:使用 ReentrantLock 的悲观锁
    public class BankAccountService {
        private final ReentrantLock lock = new ReentrantLock();
        public void transfer(Account from, Account to, BigDecimal amount) {
            lock.lock(); // 获取锁
            try {
                // 执行转账业务逻辑
                if (from.getBalance().compareTo(amount) >= 0) {
                    from.debit(amount);
                    to.credit(amount);
                }
            } finally {
                lock.unlock(); // 务必在finally块中释放锁
            }
        }
    }

⚖️ 公平锁 vs 非公平锁

选择取决于对线程执行顺序(公平性)的要求 和系统吞吐量的权衡。

  • ​公平锁适用场景 ​:需要严格保证线程不会"饥饿"​,对公平性要求高于性能的场景,如:

    • 订票系统:保证先到先得。
    • 任务调度:需要按提交顺序执行任务。
    java 复制代码
    // 示例:创建公平锁
    private final ReentrantLock fairLock = new ReentrantLock(true); // 传入true创建公平锁
  • ​非公平锁适用场景 ​:​绝大多数情况 ,为了追求更高的系统吞吐量,如:

    • 大部分业务逻辑处理。
    • 高性能服务器端应用。
    java 复制代码
    // 示例:创建非公平锁(默认行为)
    private final ReentrantLock nonFairLock = new ReentrantLock(); // 默认即为非公平锁
    // 或显式声明
    // private final ReentrantLock nonFairLock = new ReentrantLock(false);

🚀 锁性能优化实战要点

  1. 减小锁粒度:只锁必要的代码块,而不是整个方法。
  2. 缩短锁持有时间:避免在锁内执行IO、网络请求等耗时操作。
  3. 锁分离 :读多写少时,优先考虑 ReadWriteLock或 StampedLock。
  4. 无锁化设计 :对于简单的状态标记、计数器,优先使用 Atomic原子类。
  5. 避免锁嵌套:容易导致死锁。
相关推荐
IT_陈寒1 小时前
SpringBoot性能优化完全指南
前端·人工智能·后端
随性而行3601 小时前
企业微信 Webhook 接收不到消息?网络连通性、URL 签名验签与 API 返回错误码排查指南
java·网络·人工智能·后端·微信·企业微信
相隔一个图书馆的距离1 小时前
单测(三)Spring Boot 3.5.6 && @MockitoSpyBean / AOP / 循环依赖:静默吞错与运行期 NPE
java·spring boot·后端
凤山老林1 小时前
Spring Boot 集成 Apache CXF 发布与消费 SOAP WebService:WSDL、WS-Security
spring boot·后端·apache·webservice·soap·apache cxf
软件技术NINI1 小时前
基于JavaWeb 汽车租聘
java·vue.js·spring boot·后端·python·毕业设计·毕设
yz_aiks1 小时前
RestTemplate 废弃后迁移 RestClient 实战:Spring Boot 4.2 升级指南
java·spring boot·后端·微服务·restclient
金銀銅鐵11 小时前
[Java] 用 GUI 展示 EnumSet 增减元素时的内部变化(上)
java·后端·ai编程
念何架构之路11 小时前
go-grpc设计思想
开发语言·后端·golang
system_error_x12 小时前
SpringBoot 集成 Thymeleaf 简洁入门
java·spring boot·后端
步行cgn13 小时前
Spring MVC 视图机制的核心接口与类
java·开发语言·后端·spring·mvc