悲观锁与乐观锁

悲观锁

概念

悲观锁的核心思想是 "悲观" 地认为在对共享资源进行操作时,一定会有其他线程或事务来修改该资源。所以在操作之前,会先对资源进行加锁,以防止其他线程或事务的干扰,确保操作的独占性。

实现方式
  • 数据库层面 :在数据库中,常用的悲观锁实现方式是使用 FOR UPDATE 语句。例如,在 MySQL 中,执行 SELECT ... FOR UPDATE 语句会对查询结果中的记录加上行级锁,其他事务在这些记录被锁定期间无法对其进行修改或删除操作,直到当前事务提交或回滚释放锁。

sql

复制代码
-- 假设在一个事务中执行以下语句
START TRANSACTION;
SELECT * FROM users WHERE id = 1 FOR UPDATE;
-- 对查询到的记录进行修改操作
UPDATE users SET balance = balance - 100 WHERE id = 1;
COMMIT;
  • 编程语言层面 :在 Java 中,synchronized 关键字和 ReentrantLock 类都可以实现悲观锁。synchronized 关键字可以修饰方法或代码块,当一个线程进入被 synchronized 修饰的方法或代码块时,会自动获取锁,其他线程需要等待该线程释放锁才能进入。

java

复制代码
public class PessimisticLockExample {
    private int count = 0;
    private final Object lock = new Object();

    public void increment() {
        synchronized (lock) {
            count++;
        }
    }
}
使用场景

悲观锁适用于写操作频繁、冲突可能性较高的场景。例如,在银行系统中,对用户账户余额的修改操作通常使用悲观锁,以确保在一个事务对账户进行修改时,不会有其他事务同时修改该账户,避免出现数据不一致的问题。

乐观锁

概念

乐观锁持有 "乐观" 的态度,它认为在大多数情况下,多个线程或事务对共享资源的操作不会发生冲突,所以在操作之前不会加锁。只有在更新资源时,才会检查该资源是否被其他线程或事务修改过,如果没有被修改,则进行更新操作;如果被修改了,则采取相应的处理措施,如重试或报错。

实现方式
  • 版本号机制:在数据库表中增加一个版本号字段(通常为整数类型),每次对记录进行修改时,版本号会加 1。在更新记录时,会比较当前版本号与数据库中记录的版本号是否一致,如果一致则更新记录并将版本号加 1,如果不一致则表示记录已被其他事务修改,更新失败。

sql

复制代码
-- 假设 users 表中有 id、balance 和 version 字段
-- 查询记录并获取当前版本号
SELECT id, balance, version FROM users WHERE id = 1;
-- 更新记录时检查版本号
UPDATE users SET balance = balance - 100, version = version + 1 WHERE id = 1 AND version = 1;
  • 时间戳机制:与版本号机制类似,只是使用时间戳来代替版本号。每次对记录进行修改时,会更新记录的时间戳。在更新记录时,会比较当前时间戳与数据库中记录的时间戳是否一致。
使用场景

乐观锁适用于读操作频繁、写操作较少、冲突可能性较低的场景。例如,在电商系统中,商品的库存信息通常会被大量用户读取,但只有在用户下单时才会进行写操作,这种情况下可以使用乐观锁来提高系统的并发性能。

对比

  • 性能方面:悲观锁由于在操作之前就加锁,会导致其他线程或事务需要等待锁的释放,可能会降低系统的并发性能。而乐观锁在大多数情况下不需要加锁,只有在更新时才进行检查,因此在并发度较高的场景下,乐观锁的性能通常优于悲观锁。
  • 冲突处理:悲观锁通过加锁来避免冲突,保证了数据的一致性,但会增加死锁的风险。乐观锁在冲突发生时,需要进行额外的处理,如重试或报错,可能会导致一定的性能开销。
  • 使用场景:悲观锁适用于写操作频繁、冲突可能性较高的场景,而乐观锁适用于读操作频繁、写操作较少、冲突可能性较低的场景。
相关推荐
朦胧之9 小时前
AI 编程-老项目改造篇
java·前端·后端
程序猿大帅14 小时前
别再只当调包侠了:用 Spring AI 落地 Function Calling,我被大模型硬生生砸出了三个大坑
java
程序员晓琪15 小时前
约定大于配置:基于 Java 包名自动生成 API 版本路由的最佳实践
java·spring boot·后端
Flittly15 小时前
【AgentScope Java新手村系列】(11)中断与恢复
java·spring boot·spring
众少成多积小致巨15 小时前
JNI (Java Native Interface) 技术手册中文参考指南
android·java·c++
东坡白菜16 小时前
破局全栈:前端开发的Java入门实战记录—JPA(2)
java·后端
SimonKing1 天前
艹,维护AI写的代码,我心态崩了......
java·后端·程序员
用户298698530141 天前
Java Word 文档样式进阶:段落与文本背景色设置完全指南
java·后端
倔强的石头_1 天前
KingbaseES 新版MySQL 兼容版体验:旧版迁移 + 功能实测
数据库
小bo波2 天前
从"任意文件复制"深挖Java I/O:字符流与字节流的本质抉择
java·nio·io流·后端开发·文件复制