分布式微服务系统架构第140集:锁机制一览

65 阅读4分钟

加群联系作者vx:xiaoda0423

仓库地址:webvueblog.github.io/JavaPlusDoc…

1024bat.cn/

github.com/webVueBlog/…

webvueblog.github.io/JavaPlusDoc…


📢 联系与资源


锁机制一览

分类Java 实现/示例特点适用场景
悲观锁synchronizedReentrantLock(默认)全程加锁,阻塞等待,开销大高冲突写场景
乐观锁数据库 version 字段+CAS无锁操作,提交时校验,冲突回退冲突少、读多写少
分布式锁Redis、ZooKeeper 实现集群环境下的全局互斥分布式系统资源争夺
可重入锁ReentrantLocksynchronized同一线程可重复获得,避免自死锁嵌套调用需要二次加锁
自旋锁while(tryLock()){}不阻塞线程,循环尝试,占用 CPU临界区极短、线程冲突不激烈
独享锁ReentrantLock、写锁单线程占有,互斥执行写操作独占
共享锁ReentrantReadWriteLock 读锁多线程共享,无阻塞读;写时独占读多写少、高并发读
公平锁new ReentrantLock(true)按请求顺序分配,防止饥饿对响应时序要求严格
非公平锁默认 ReentrantLock()synchronized吞吐量高,可能造成线程饥饿性能优先,偶尔可容忍饥饿
分段锁JDK7 ConcurrentHashMap将数据分段锁,细化锁粒度大规模并发 Map 操作
阻塞锁lock()await()线程阻塞挂起,唤醒后抢占需等待条件满足、阻塞式协调
信号量Semaphore控制多线程对多个资源的并发访问限流、线程池可用许可管理
条件变量Condition替代 wait/notify,可精确唤醒指定线程复杂线程通信、分组唤醒
行级锁SQL UPDATE … WHERE数据库内部对记录加锁,粒度最细,高性能高并发下库存扣减、状态流转

1. 悲观锁(Pessimistic Lock)

  • 原理:在读写期间始终加锁,其他线程必须等待。

  • 实现

    • Java:synchronized(重量级锁)
    • Java:ReentrantLock 默认即悲观锁
  • 优缺点

    • ✅ 安全、简单
    • ❌ 长锁占用导致并发度低,阻塞严重
  • 示例

    synchronized(this) {
      // 处理临界区
    }
    

2. 乐观锁(Optimistic Lock)

  • 原理:不加锁,读时取得版本号,写时通过 CAS 验证版本,无冲突则更新。

  • 实现:常见于数据库,表中增加 version 字段,更新语句:

    UPDATE table
    SET col = ?, version = version + 1
    WHERE id = ? AND version = ?
    
  • 优缺点

    • ✅ 无锁开销,读写几乎不阻塞
    • ❌ 冲突时需回滚重试,编程复杂
  • 适用:冲突少、读多写少场景,如用户资料更新。


3. 分布式锁(Distributed Lock)

  • 原理:借助外部系统(Redis、ZooKeeper、Etcd)维护全局锁。

  • 实现

    • Redis:SET key value NX PX timeout
    • ZooKeeper:EPHEMERAL + 顺序节点
  • 优缺点

    • ✅ 可跨服务/跨语言互斥
    • ❌ 需处理单点、性能和超时等问题
  • 适用:微服务间资源争抢,如生成唯一订单号。


4. 可重入锁(Reentrant Lock)

  • 原理:同一线程可多次获得同一把锁,锁计数器递增/递减。

  • 实现

    • Java:synchronized(内置可重入)
    • Java:ReentrantLock
  • 优点:避免递归调用、嵌套方法中的死锁。


5. 自旋锁(Spin Lock)

  • 原理:线程不断轮询尝试获取锁,不进入阻塞状态。

  • 实现

    while (!lock.tryLock()) {
      // 自旋
    }
    try { … } finally { lock.unlock(); }
    
  • 优缺点

    • ✅ 不切换线程上下文,低延迟
    • ❌ CPU 占用高,适合临界区极短的场景。

6. 读写锁(Read–Write Lock)

  • 原理:将锁分为读锁(共享)和写锁(独占)。

  • 实现ReentrantReadWriteLock

  • 优缺点

    • ✅ 多线程并发读,高效无阻塞
    • ❌ 写锁独占,升级/降级逻辑复杂
  • 示例

    ReadWriteLock rw = new ReentrantReadWriteLock();
    rw.readLock().lock();
    // 读操作
    rw.readLock().unlock();
    

7. 公平锁 vs 非公平锁

类型分配策略吞吐量典型实现
公平锁按请求顺序(FIFO)分配较低new ReentrantLock(true)
非公平锁抢占式,可能插队较高默认 synchronizedReentrantLock()

8. 分段锁(Segment Lock)

  • 原理:把大数据结构分段,每段独立加锁,降低冲突域。

  • 实现

    • JDK7 ConcurrentHashMapSegment 继承 ReentrantLock
  • 适用:大规模并发 Map 操作。


9. 信号量(Semaphore)

  • 原理:内部计数器控制可并发访问资源数。

  • Java 实现java.util.concurrent.Semaphore

  • 示例

    Semaphore sem = new Semaphore(3);
    sem.acquire();  // 获取许可
    try { … } finally { sem.release(); }
    

10. 条件变量(Condition)

  • 原理:替代 wait/notify,多条件队列,可精确唤醒。

  • Java 实现Lock.newCondition()

  • 示例

    Lock lock = new ReentrantLock();
    Condition cond = lock.newCondition();
    lock.lock();
    cond.await();      // 等待
    cond.signalAll();  // 唤醒
    lock.unlock();
    

11. 行级锁(Row Lock)

  • 原理:数据库引擎对单行记录加锁,粒度最细。

  • 示例

    UPDATE inventory
    SET amount = amount - 1
    WHERE id = ? AND amount > 0;
    
  • 优点:并发度最高,适合高并发库存扣减、状态流转。


选型建议

  • 冲突多、写密集:悲观锁或分布式锁
  • 读多写少:乐观锁 + 读写锁
  • 分布式环境:Redis/ZooKeeper 分布式锁
  • 高并发读写:分段锁 + 读写锁
  • 限流场景:信号量