1. 概述

本文将带你深入了解 Binary Semaphore(二值信号量)Reentrant Lock(可重入锁) 的使用场景与核心差异。我们将从机制、所有权、灵活性等多个维度进行对比,帮助你判断在不同场景下应该选择哪种同步工具。

2. 什么是 Binary Semaphore?

Binary Semaphore 是一种用于控制单一资源访问的同步机制。它本质上是一种互斥机制,只允许一个线程进入临界区

Binary Semaphore 内部只维护一个许可(permit),因此它只有两种状态:

  • ✅ 1 个许可可用(未被占用)
  • ❌ 0 个许可可用(已被占用)

我们可以通过 Java 中的 Semaphore 类来实现一个 Binary Semaphore:

Semaphore binarySemaphore = new Semaphore(1);
try {
    binarySemaphore.acquire();
    assertEquals(0, binarySemaphore.availablePermits());
} catch (InterruptedException e) {
    e.printStackTrace();
} finally {
    binarySemaphore.release();
    assertEquals(1, binarySemaphore.availablePermits());
}
  • acquire() 方法会消耗一个许可
  • release() 方法会释放一个许可

⚠️ Semaphore 还支持 fairness 参数。设置为 true 时,将按照线程等待时间顺序分配许可,防止线程饥饿:

Semaphore binarySemaphore = new Semaphore(1, true);

3. 什么是 Reentrant Lock?

Reentrant Lock 是一种可重入的互斥锁机制,允许同一个线程多次获取锁而不会导致死锁

每次线程成功获取锁时,锁的持有计数器 hold count 会加一;每次释放锁时,计数器减一。只有当计数器归零时,锁才真正被释放

示例代码如下:

ReentrantLock reentrantLock = new ReentrantLock();
try {
    reentrantLock.lock();
    assertEquals(1, reentrantLock.getHoldCount());
    assertEquals(true, reentrantLock.isLocked());
} finally {
    reentrantLock.unlock();
    assertEquals(0, reentrantLock.getHoldCount());
    assertEquals(false, reentrantLock.isLocked());
}

如果线程多次获取锁,必须同样多次释放锁才能真正解锁:

reentrantLock.lock();
reentrantLock.lock();
assertEquals(2, reentrantLock.getHoldCount());
assertEquals(true, reentrantLock.isLocked());

reentrantLock.unlock();
assertEquals(1, reentrantLock.getHoldCount());
assertEquals(true, reentrantLock.isLocked());

reentrantLock.unlock();
assertEquals(0, reentrantLock.getHoldCount());
assertEquals(false, reentrantLock.isLocked());

同样,ReentrantLock 也支持公平锁模式:

ReentrantLock reentrantLock = new ReentrantLock(true);

4. Binary Semaphore vs Reentrant Lock 对比

4.1. 工作机制 ✅

特性 Binary Semaphore Reentrant Lock
类型 信号机制(signaling mechanism) 锁机制(locking mechanism)
用途 控制资源访问 提供互斥保护

4.2. 所有权 ✅

特性 Binary Semaphore Reentrant Lock
是否有所有者 ❌ 无所有者 ✅ 有明确所有者(最后获取锁的线程)

4.3. 可重入性 ✅

特性 Binary Semaphore Reentrant Lock
是否支持重入 ❌ 不支持(重入会导致死锁) ✅ 支持(同一线程可多次获取)

4.4. 灵活性 ✅

特性 Binary Semaphore Reentrant Lock
是否灵活 ✅ 高(可自定义同步逻辑) ❌ 低(固定锁机制)

4.5. 修改权限 ✅

特性 Binary Semaphore Reentrant Lock
谁能释放 ✅ 任意线程 ❌ 仅持有锁的线程

4.6. 死锁恢复 ✅

特性 Binary Semaphore Reentrant Lock
是否支持非所有者释放 ✅ 是(可用于死锁恢复) ❌ 否(死锁恢复困难)

5. 总结

简单来说:

  • Binary Semaphore 是一种无所有权的信号机制,适合需要灵活控制并发访问的场景,也便于实现自定义锁与死锁恢复。
  • Reentrant Lock 是一种基于所有者的可重入锁,适合需要严格互斥控制的场景,尤其适用于替代 synchronized 的高性能锁。

📌 建议使用场景

  • 如果你需要灵活控制并发访问、支持任意线程释放资源,选 Binary Semaphore。
  • 如果你只是想保护一段代码的互斥访问,并且希望支持重入,选 Reentrant Lock。

源码示例可参考:GitHub 项目地址


原始标题:Binary Semaphore vs Reentrant Lock | Baeldung