- tryAcquire(int arg)
- acquire(int arg)
- tryRelease(int arg)
- release(int arg)
1. AQS 内部状态 state
-
state 变量:
AQS 内部通过一个volatile int state
来记录当前同步状态。- 对于简单的非重入锁,通常 state 为 0 表示锁处于空闲状态,为 1 表示锁被占用。
- 对于重入锁,state 可以累加,用来记录同一线程重入的次数。
-
CAS 操作:
AQS 使用CAS操作来原子性地修改 state,从而保证并发场景下状态更新的安全性。
2. tryAcquire(int arg)
-
作用:
- 在这个方法中,会根据当前 state 的值判断能否获得锁。
- 如果 state 为 0(锁空闲),则尝试通过 CAS 操作将 state 更新为非 0(例如 1 或者 arg 表示的值),同时记录当前线程为锁的持有者。
- 如果是重入场景,并且当前线程已经持有锁,则可以让 state 累加,允许重入。
-
示例代码(非重入锁):
protected boolean tryAcquire(int acquires) { // 如果 state 为 0,尝试获取锁 if (compareAndSetState(0, acquires)) { // 设置当前线程为独占线程 setExclusiveOwnerThread(Thread.currentThread()); return true; } return false; }
在这个过程中,
compareAndSetState(0, acquires)
就是对 state 的显式设置,它保证只有一个线程能成功更新 state,从而获得锁。
3. acquire(int arg)
-
作用:
- 当线程调用
acquire(arg)
时,AQS 会先调用tryAcquire(arg)
。 - 如果
tryAcquire(arg)
返回true
,说明锁已成功获取; - 否则,线程会被加入到 AQS 内部的 FIFO 队列中,进入等待状态,直到获取锁。
- 当线程调用
-
工作流程:
- 调用
tryAcquire(arg)
尝试获取锁。 - 如果失败,调用
addWaiter(Node.EXCLUSIVE)
将线程包装成节点添加到等待队列。 - 进入自旋或阻塞状态,等待前驱节点释放锁并唤醒自己,最终再次尝试获取锁。
- 调用
4. tryRelease(int arg)
-
作用:
- 它会根据当前 state 的值来判断是否能释放锁。
- 对于重入锁,通常是将 state 减少
arg
,当 state 减少到 0 时,表示完全释放锁,并清除当前线程的持有标记。
-
示例代码(非重入锁):
protected boolean tryRelease(int releases) { // 对于非重入锁,直接判断 state 是否为 0 if (getState() == 0) { throw new IllegalMonitorStateException(); } // 释放锁,将 state 设为 0,并清除当前持有线程 setExclusiveOwnerThread(null); setState(0); return true; }
5. release(int arg)
-
作用:
- 它内部会调用开发者实现的
tryRelease(arg)
。 - 如果
tryRelease(arg)
返回true
,表示锁完全释放,那么 AQS 会唤醒等待队列中的下一个线程。
- 它内部会调用开发者实现的
-
工作流程:
- 调用
tryRelease(arg)
尝试释放锁。 - 如果返回 true,说明锁释放成功,接下来调用
unparkSuccessor(Node node)
来唤醒等待队列中的后继节点,从而使其他等待线程有机会重新竞争锁。
- 调用