DEV Community

Woody
Woody

Posted on

Lock in Java and SQL

一文讲透 Java 和数据库中的锁机制

锁是并发编程和数据库管理中保证数据一致性与安全性的核心机制。它们虽存在于不同层面,但原理相似:在资源被使用时,控制访问权,避免冲突和数据错误


一、Java 中的锁机制

1️⃣ 锁的目的

  • 保证多线程访问共享资源时的数据一致性
  • 避免竞态条件(race condition)
  • 控制线程同步和调度

2️⃣ 锁的分类

(1)悲观锁(Pessimistic Lock)

  • 概念:假设其他线程会并发修改,所以操作前就加锁。
  • 实现方式

    • synchronized(Java 内置锁)
    • ReentrantLock(可重入锁)
  • 特点

    • 阻塞式,线程需要等待锁释放
    • 适合写多冲突大的场景
  • 示例

class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}
Enter fullscreen mode Exit fullscreen mode

(2)乐观锁(Optimistic Lock)

  • 概念:假设其他线程不会冲突,操作时检查是否被修改。
  • 实现方式

    • 版本号控制(version field)
    • CAS(Compare-And-Swap / Compare-And-Set)
  • 特点

    • 无锁或轻量锁
    • 适合读多写少的场景
  • 示例(版本号)

class OptimisticCounter {
    private int count = 0;
    private int version = 0;

    public boolean increment() {
        int oldVersion = version;
        int oldCount = count;
        int newCount = oldCount + 1;
        if (oldVersion == version) {
            count = newCount;
            version++;
            return true;
        } else {
            return false; // 冲突,需要重试
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

(3)CAS(Compare-And-Swap)

  • 概念:原子操作,通过比较旧值和期望值来决定是否更新
  • 特点

    • 无锁,性能高
    • 可能发生 ABA 问题,需要版本号解决
  • 示例(AtomicInteger)

import java.util.concurrent.atomic.AtomicInteger;

class CASCounter {
    private AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        int oldValue;
        int newValue;
        do {
            oldValue = count.get();
            newValue = oldValue + 1;
        } while (!count.compareAndSet(oldValue, newValue));
    }

    public int getCount() {
        return count.get();
    }
}
Enter fullscreen mode Exit fullscreen mode

(4)其他锁

锁类型 说明 适用场景
读写锁(ReadWriteLock) 允许多个线程读,但写互斥 读多写少
偏向锁、轻量锁、重量锁(JVM 锁优化) JVM 内部锁优化 减少无冲突锁的开销

二、数据库中的锁机制

数据库锁是为了保证事务的 ACID 特性(特别是隔离性),常用在关系型数据库如 MySQL、Oracle、PostgreSQL。

1️⃣ 锁的目的

  • 防止脏读、不可重复读、幻读
  • 保证事务隔离和数据一致性

2️⃣ 锁的分类

(1)按加锁粒度

  • 表级锁(Table Lock):锁整个表,简单但性能低
  • 行级锁(Row Lock / Record Lock):只锁某行,性能高但开销大

(2)按锁类型

类型 作用 典型用途
排它锁(Exclusive / X Lock) 只允许自己读写 写操作
共享锁(Shared / S Lock) 多个事务可以共享读 读操作
意向锁(Intention Lock) 表示事务将要在行上加锁 行级锁配合表锁使用
乐观锁 通过版本号或时间戳检查冲突 适合高并发更新
悲观锁 使用 SELECT … FOR UPDATE 防止并发修改

(3)数据库锁机制示例(MySQL InnoDB)

  • 悲观锁
SELECT * FROM users WHERE id=1 FOR UPDATE; 
-- 给查询行加排它锁,直到事务提交
Enter fullscreen mode Exit fullscreen mode
  • 乐观锁
UPDATE users 
SET balance = balance + 100, version = version + 1
WHERE id = 1 AND version = 5;
-- version 检查是否被其他事务修改
Enter fullscreen mode Exit fullscreen mode

三、Java 锁 vs 数据库锁对比

维度 Java 锁 数据库锁
锁的对象 内存中的对象 数据库表、行
控制方式 JVM / OS 数据库引擎
粒度 对象、字段、方法 表、行
实现机制 synchronized、ReentrantLock、CAS X/S 锁、行锁、表锁、MVCC
适用场景 并发编程、共享内存 事务管理、并发数据操作
性能 高(轻量锁) 较低(I/O 开销大)

四、总结与实用建议

  1. Java 层
  • 写多用悲观锁,读多写少用乐观锁 + CAS
  • 尽量使用原子类(AtomicInteger)或 ReadWriteLock 提升性能

    1. 数据库层
  • 读操作尽量少用锁,使用 MVCC(MySQL InnoDB 默认)

  • 写操作冲突大时使用悲观锁,少量并发可用乐观锁

    1. 全链路设计
  • 高并发系统,Java 层用 CAS/乐观锁 + 数据库乐观锁

  • 写冲突低,优先乐观锁提高吞吐量


Top comments (0)