在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著)
原创文章,发布者:酷盾叔,转转请注明出处:https://www.kd.cn/ask/21362.html