当前位置:首页 > 后端开发 > 正文

如何在Java中添加行锁?

在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; } } } }

原理

如何在Java中添加行锁? 第1张

  • 每个对象内置一个监视器锁(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(); // 必须释放锁 } } }

优势

如何在Java中添加行锁? 第2张

  • 支持公平锁、超时尝试(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)
读多写少、冲突概率低 乐观锁(版本控制)


避坑指南

  1. 死锁预防

    • 代码锁:按固定顺序获取锁(如按对象ID排序)。
    • 数据库锁:设置锁超时(innodb_lock_wait_timeout)。
  2. 性能优化

    • 缩小锁范围(减少临界区代码)。
    • 无锁化设计(如 ConcurrentHashMap)。
  3. 分布式环境

    • 使用分布式锁(Redis Redlock、ZooKeeper)。
    • 避免数据库行锁跨服务失效。
      • 代码级”行锁”:通过 synchronized 或 ReentrantLock 锁定对象实例。
      • 数据库行锁:依赖SQL的 FOR UPDATE 或乐观锁实现。
      • 核心原则:最小化锁范围、预防死锁、匹配业务场景。

      引用说明

      • Oracle官方Java并发指南:Threads and Locks
      • MySQL行锁机制:InnoDB Locking
      • 并发实践参考:《Java并发编程实战》(Brian Goetz著)

      如何在Java中添加行锁? 第3张

0