上一篇
如何在Java中添加行锁?
- 后端开发
- 2025-06-12
- 2112
在Java中,行锁通常通过synchronized关键字或ReentrantLock实现,synchronized修饰方法或代码块,确保同一时刻仅一个线程执行;ReentrantLock需手动lock()/unlock(),提供更灵活的锁控制。
在Java中实现”行锁”通常有两种场景:代码层面的对象锁(模拟行锁行为)和数据库行锁,下面从技术原理、实现方式和最佳实践进行详细说明,确保内容符合专业性与可靠性标准。
代码层面的对象锁(模拟行锁)
当需要锁定单个对象实例(类比数据库中的一行数据)时,可通过以下方式实现:
synchronized 关键字
public class Account {
private final Object lock = new Object(); // 专用锁对象
private int balance;
public void transfer(Account target, int amount) {
// 锁定当前对象(模拟行锁)
synchronized (this) {
// 锁定目标对象
synchronized (target) {
this.balance -= amount;
target.balance += amount;
}
}
}
}
原理:
- 每个对象内置一个监视器锁(Monitor),
synchronized基于此实现互斥。 - 锁定
this或自定义锁对象,相当于对”行”(对象实例)加锁。
注意事项:

- 避免嵌套锁导致的死锁(如上例需按固定顺序加锁)。
- 锁范围应尽量缩小(仅在临界区加锁)。
ReentrantLock 显式锁
import java.util.concurrent.locks.ReentrantLock;
public class RowLockExample {
private final ReentrantLock lock = new ReentrantLock();
private int data;
public void updateData(int value) {
lock.lock(); // 加锁
try {
data = value; // 临界区操作
} finally {
lock.unlock(); // 必须释放锁
}
}
}
优势:
- 支持公平锁、超时尝试(
tryLock)、可中断等待。 - 更灵活的锁控制。
数据库行锁(Java中操作)
当锁的目标是数据库中的行记录时,需通过SQL实现:
悲观锁(SELECT FOR UPDATE)
// JDBC 示例(以MySQL为例)
Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
conn.setAutoCommit(false);
try {
// 锁定id=100的行
PreparedStatement stmt = conn.prepareStatement(
"SELECT * FROM accounts WHERE id = ? FOR UPDATE"
);
stmt.setInt(1, 100);
ResultSet rs = stmt.executeQuery();
// 更新操作
PreparedStatement updateStmt = conn.prepareStatement(
"UPDATE accounts SET balance = balance - 100 WHERE id = ?"
);
updateStmt.setInt(1, 100);
updateStmt.executeUpdate();
conn.commit(); // 提交事务释放锁
} catch (SQLException e) {
conn.rollback();
}
关键点:

FOR UPDATE在事务中锁定查询到的行。- 事务提交/回滚后自动释放锁。
- 需配合事务隔离级别(推荐
READ_COMMITTED或REPEATABLE_READ)。
乐观锁(版本控制)
// 更新时检查版本号 UPDATE products SET stock = stock - 1, version = version + 1 WHERE id = 123 AND version = 5;
原理:
- 读取数据时记录版本号(或时间戳)。
- 更新时校验版本号,失败则重试或抛异常。
- 适合低冲突场景,避免长期锁竞争。
如何选择锁方案?
| 场景 | 推荐方案 |
|---|---|
| 单JVM内的对象互斥 | synchronized 或 ReentrantLock |
| 高并发数据库写操作 | 悲观锁(FOR UPDATE) |
| 读多写少、冲突概率低 | 乐观锁(版本控制) |
避坑指南
-
死锁预防:
- 代码锁:按固定顺序获取锁(如按对象ID排序)。
- 数据库锁:设置锁超时(
innodb_lock_wait_timeout)。
-
性能优化:

- 缩小锁范围(减少临界区代码)。
- 无锁化设计(如
ConcurrentHashMap)。
-
分布式环境:
- 使用分布式锁(Redis Redlock、ZooKeeper)。
- 避免数据库行锁跨服务失效。
- 代码级”行锁”:通过
synchronized或ReentrantLock锁定对象实例。 - 数据库行锁:依赖SQL的
FOR UPDATE或乐观锁实现。 - 核心原则:最小化锁范围、预防死锁、匹配业务场景。
引用说明:
- Oracle官方Java并发指南:Threads and Locks
- MySQL行锁机制:InnoDB Locking
- 并发实践参考:《Java并发编程实战》(Brian Goetz著)
