Java高频面试之并发编程-23

hello啊,各位观众姥爷们!!!本baby今天又来报道了!哈哈哈哈哈嗝🐶

面试官:ReentrantLock 如何实现公平锁的?


ReentrantLock 公平锁的实现原理

ReentrantLock 的公平锁通过 AQS(AbstractQueuedSynchronizer) 的同步队列(CLH 队列变体)和 严格的顺序检查机制 实现,确保线程按照请求锁的顺序获取锁。以下是其核心实现细节:


1. 公平锁与非公平锁的核心区别

公平锁与非公平锁的唯一差异在于 获取锁时的策略:

  • 非公平锁:新线程可以直接尝试抢占锁(即使队列中有线程在等待)。
  • 公平锁:新线程必须检查队列中是否有等待线程,若有则排队,严格按 FIFO 顺序获取锁。

2. 公平锁的实现关键

(1) tryAcquire 方法的重写

公平锁的 tryAcquire 方法中,必须先检查同步队列中是否有其他线程在排队 ,若无才允许尝试获取锁。这是通过 hasQueuedPredecessors() 方法实现的。

java 复制代码
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 关键点:只有队列中没有等待线程时,才尝试获取锁
        if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) {
        // 重入逻辑与非公平锁一致
        int nextc = c + acquires;
        setState(nextc);
        return true;
    }
    return false;
}
(2) hasQueuedPredecessors() 方法

该方法判断当前线程是否是队列中的第一个有效节点(即是否有其他线程更早地请求锁):

java 复制代码
public final boolean hasQueuedPredecessors() {
    Node t = tail; // 队列尾节点
    Node h = head; // 队列头节点
    Node s;
    // 队列不为空,且头节点的下一个节点存在,且该节点关联的线程不是当前线程
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}
  • 返回 true:队列中存在比当前线程更早等待的线程,当前线程必须排队。
  • 返回 false:队列为空或当前线程是队列中的第一个有效节点,允许尝试获取锁。

3. 公平锁的工作流程

(1) 线程首次获取锁
  1. 线程调用 lock() 方法。
  2. 调用 acquire(1),触发 tryAcquire。
  3. 若 state=0(锁未被占用):
    • 检查队列中是否有等待线程(hasQueuedPredecessors())。
    • 若无等待线程 :尝试 CAS 修改 state 为 1,成功则获取锁。
    • 若有等待线程 :封装为 Node 加入队列尾部,进入阻塞状态。
(2) 锁释放后唤醒后续线程
  1. 持有锁的线程调用 unlock(),释放锁(state=0)。
  2. AQS 唤醒队列中第一个有效节点(头节点的后继节点)关联的线程。
  3. 被唤醒的线程再次尝试 tryAcquire,此时队列中无更早的等待线程,成功获取锁。

4. 公平锁的严格顺序保证

公平锁通过以下机制确保顺序性:

  1. 禁止插队:新线程必须检查队列中是否有等待线程,无法直接抢占锁。
  2. 队列唤醒:锁释放时,只唤醒队列中的第一个有效节点,确保先进先出。

5. 公平锁的代价

  • 性能开销 :
    每次获取锁都需要检查队列状态,增加 CAS 操作和线程切换次数。
  • 吞吐量降低 :
    严格的顺序性可能导致 CPU 空闲(例如,队列中的线程唤醒需要时间)。

6. 代码示例:公平锁的完整获取流程

java 复制代码
// 创建公平锁
ReentrantLock fairLock = new ReentrantLock(true);

// 加锁过程
fairLock.lock();
try {
    // 临界区代码
} finally {
    fairLock.unlock();
}

你想要的技术资料我全都有:https://pan.q删掉汉子uark.cn/s/aa7f2473c65b


相关推荐
ShineWinsu16 小时前
对于Redis:Steam、Geospatial、Hyperloglog、Bitmap、Bitfield类型的解析
数据库·c++·redis·分布式·缓存·面试·zset
weixin_4407305016 小时前
迭代器Iterator(列表生成器、可迭代的如何生成迭代器)
开发语言·python
朝朝辞暮i16 小时前
C++ 第 31C 课:Subscriber 到底是什么
开发语言·c++·算法
海绵宝宝转agent16 小时前
操作系统八股开源笔记分享
java·面试
weixin_4407305017 小时前
内置函数、json文本、pickle二进制
开发语言·python·json
殷色玫瑰17 小时前
C++ string类详解:常用接口、字符串操作与模拟实现
java·linux·c语言·开发语言·数据结构·c++
李游Leo17 小时前
HarmonyOS 7 + Spatial Recon Kit-C++:3DGS 高斯参数的非有限值隔离与可渲染性门禁【鸿蒙心迹】
开发语言·c++·3d·harmonyos
CHEEVEN_QY17 小时前
新能源汽车电池托盘FSW量产的工艺稳定性控制
java·前端·汽车
惊讶的猫17 小时前
MCP-server
java
ZealSinger17 小时前
Boot4升级PostConstruct不跑了
java·spring boot·spring·升级迁移