悲观锁与乐观锁

悲观锁

概念

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

实现方式
  • 数据库层面 :在数据库中,常用的悲观锁实现方式是使用 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;
  • 时间戳机制:与版本号机制类似,只是使用时间戳来代替版本号。每次对记录进行修改时,会更新记录的时间戳。在更新记录时,会比较当前时间戳与数据库中记录的时间戳是否一致。
使用场景

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

对比

  • 性能方面:悲观锁由于在操作之前就加锁,会导致其他线程或事务需要等待锁的释放,可能会降低系统的并发性能。而乐观锁在大多数情况下不需要加锁,只有在更新时才进行检查,因此在并发度较高的场景下,乐观锁的性能通常优于悲观锁。
  • 冲突处理:悲观锁通过加锁来避免冲突,保证了数据的一致性,但会增加死锁的风险。乐观锁在冲突发生时,需要进行额外的处理,如重试或报错,可能会导致一定的性能开销。
  • 使用场景:悲观锁适用于写操作频繁、冲突可能性较高的场景,而乐观锁适用于读操作频繁、写操作较少、冲突可能性较低的场景。
相关推荐
小陈不好吃10 分钟前
深入理解 JVM 内存模型:从堆栈结构到 GC 日志分析实战
java·jvm·java-ee·jdk
爱吃苹果的日记本14 分钟前
JAVA十三(练习课)
java·学习
磐链科技14 分钟前
交易所开发全流程指南:从需求分析到上线运维的完整路线图
java
無限進步D17 分钟前
Java Web 前端 简介
java·开发语言·前端·css·html·css3·web
聪明蛋子哟18 分钟前
从LangChain到LangGraph:Python与Java双栈Agent开发实战对比
java·ai·langchain
qq_4017004137 分钟前
Qt QUrl 详解与代码示例
开发语言·数据库·qt
三84439 分钟前
Java 模板注入(FreeMarker)· 04 · 利用与绕过:payload 全集
java·web安全·freemarker·payload
leikooo1 小时前
ARTS 0913: 双栈互补实现队列、CIDR 聚合化解路由膨胀与 AI 作弊串通绝非偶然 Bug
java·链表·个人开发
程序员阿鹏1 小时前
如何实现MySQL分库分表?
数据结构·数据库·sql·mysql·缓存
星间都市山脉1 小时前
Android16 SystemService.onBootPhase 调用时机
android·java·linux·windows·ubuntu