解释乐观锁和悲观锁的概念,并在 Java 中如何实现这两种锁机制,分别举一个简单的例子?

乐观锁和悲观锁是两种处理并发控制的不同策略,它们各有侧重,适用于不同的场景。下面我会用生活化的例子来帮助你理解这两种锁机制,并展示在Java中如何简单地实现它们。

乐观锁

概念:乐观锁假定在大部分情况下,数据不会发生并发冲突,所以在操作数据前不会加锁,只是在更新数据的时候检查在此期间数据是否被其他事务修改过。乐观锁的核心在于"先做事,后检查"。

例子:想象你和朋友一起去图书馆借书,乐观锁的策略就像是你认为这本书多半没人借,所以你直接去前台登记借书,登记时再确认书有没有被别人借走。如果书被借走了,你就重新找一本或者等待。

Java实现:乐观锁通常借助版本号或时间戳来实现。比如,我们在数据库表中添加一个版本字段version,每次更新数据时,同时更新版本号。

复制代码
1public class Book {
2    private int id;
3    private String title;
4    private int version; // 版本号
5
6    // 更新书籍的方法,使用乐观锁
7    public boolean updateTitle(String newTitle) {
8        // 假设这里有一段SQL,类似 "UPDATE book SET title = ?, version = version + 1 WHERE id = ? AND version = ?"
9        // 如果更新的行数为0,说明版本号不对,即数据已被其他人修改
10        int updatedRows = dao.updateTitleAndVersion(id, newTitle, version);
11        if (updatedRows > 0) {
12            version++; // 如果更新成功,本地版本号也需要+1
13            return true;
14        }
15        return false;
16    }
17}

悲观锁

概念:悲观锁采取较为保守的态度,认为数据在操作过程中很可能会被其他事务修改,因此在操作数据前就会加锁,阻止其他事务的修改操作,直到当前事务完成。悲观锁的核心思想是"先拿锁,后做事"。

例子:还是借书的情景,悲观锁就像是你担心书随时会被别人借走,所以你一开始就拿着这本书去柜台排队,直到借书手续完成才放手,期间别人不能借走这本书。

Java实现:在Java中,悲观锁的实现通常依赖于数据库层面的锁机制,如行级锁。使用JDBC时,可以通过设置事务隔离级别和使用SELECT ... FOR UPDATE来实现悲观锁。

复制代码
1Connection conn = dataSource.getConnection();
2try {
3    conn.setAutoCommit(false); // 开启事务
4    PreparedStatement ps = conn.prepareStatement("SELECT * FROM book WHERE id = ? FOR UPDATE");
5    ps.setInt(1, bookId);
6    ResultSet rs = ps.executeQuery();
7    if (rs.next()) {
8        // 处理数据,比如检查库存、更新信息等
9        // ...
10        
11        // 执行更新操作
12        // ...
13        
14        conn.commit(); // 提交事务
15    }
16} catch (SQLException e) {
17    conn.rollback(); // 出错回滚事务
18} finally {
19    conn.close();
20}
  • 乐观锁适用于并发冲突较少的场景,它通过版本控制减少锁定时间,提高并发性能,但需要额外的逻辑来处理冲突。
  • 悲观锁则适用于并发冲突频繁的场景,它通过提前加锁确保数据一致性,但可能会导致较高的锁竞争和等待时间,影响并发性能。

选择哪种锁,取决于你对数据并发访问的预期和对性能的要求。在实际应用中,还需要根据具体业务场景和数据库特性综合考虑。

相关推荐
一只小小Java1 分钟前
Naocs本地部署&安装3.2.3+Spring boot 3.2.0
java·spring boot·后端·nacos
AAA@峥7 分钟前
系统化学习 MySQL:数据类型、库表管理、增删改查全解析
数据库·学习·mysql
JL157 分钟前
Spring Boot vs Gin:Java 人搭第一个 Go REST API
java·spring boot·gin
神明不懂浪漫1 小时前
【第七章】Java中的常用类
java·开发语言·前端·经验分享·笔记
流星白龙7 小时前
【Redis】2.Redis重大版本
数据库·redis·junit
流星白龙7 小时前
【Redis】7.Hash表
数据库·redis·哈希算法
流星白龙8 小时前
【Redis】4.基本全局命令
数据库·redis·缓存
程序喵大人9 小时前
【C++进阶】STL容器与迭代器 - 01 STL 容器先解决元素放在哪里
开发语言·c++·stl
王八八。9 小时前
Navicat 17破解版下载安装教程 附安装激活步骤(2026 最新版)
数据库·navicat
cui_ruicheng9 小时前
Python从入门到实战(十六):多进程编程
开发语言·python