AQS
AQS全称(AbstractQueuedSynchronizer)抽象队列同步器,是JUC包下的基础框架
常见的实现类有ReentrantLock、CountDownLatch、Semaphore 等
AQS的核心机制
-
state变量:用volatile修饰的int类型的state变量,volatile保证了线程间的可见性,当state = 0 无锁, state = 1 加锁
/** * The synchronization state. */ private volatile int state;
-
FIFO的阻塞队列:底层维护了一个FIFO的阻塞队列(双向链表),head指向队列中的头节点,tail指向队列中的尾节点、Node节点
private transient volatile Node head; private transient volatile Node tail; ``` Node节点中主要包含 两个指针 和 线程信息 ```java static final class Node { volatile int waitStatus; //线程等待状态 volatile Node prev; //指向前一个NOde volatile Node next; //指向后一个Node volatile Thread thread; // 保存等待的线程 Node nextWaiter; //下一个等待的 } ```
-
CAS操作:当修改state值时,使用CAS操作,保证操作的原子性
protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }
修改state 变量的get、set方法的都是fina 修饰的,无法重写,而且是 CAS 机制进行同步。如果是可重入锁,如果 ReentrantLock,每次重入都会将 state 的变量值+1,因此需要多次释放让 State 重新归零,才释放锁。
AQS执行流程
独占模式
在独占模式下,同一时间只有一个线程可以持有锁或同步状态。
-
获取锁:
- 线程尝试调用
acquire(int arg)
方法获取锁。 - 该方法内部会首先调用
tryAcquire(arg)
尝试以原子方式更新state
值。如果成功(即state
从0变为非零值),则该线程获取到锁并继续执行;若失败(例如state
已经不是0),则将当前线程封装成节点加入到同步队列尾部,并进入等待状态。 - 在比如重入锁(ReentrantLock)情况下,
state
的值会随着同一个线程多次获取锁而增加,表明锁被重入的次数。
- 线程尝试调用
-
释放锁:
- 当持有锁的线程准备释放锁时,会调用
release(int arg)
方法。 - 此方法会调用
tryRelease(arg)
减少state
的值。当state
回到0时,意味着锁已被完全释放,这时会唤醒同步队列中的下一个节点对应的线程,让其有机会获取锁。
- 当持有锁的线程准备释放锁时,会调用
共享模式
在共享模式下,允许多个线程同时持有锁或同步状态,但具体能有多少线程同时持有取决于具体的同步器实现。
-
获取锁:
- 线程调用
acquireShared(int arg)
方法尝试获取锁。 - 方法内部会调用
tryAcquireShared(int arg)
尝试更新state
值。如果返回值大于等于0,则表示获取成功,允许其他线程也尝试获取锁;如果返回值小于0,则当前线程无法获取锁,需要将自己封装成节点加入到同步队列尾部并等待。
- 线程调用
-
释放锁:
- 持有锁的线程调用
releaseShared(int arg)
方法释放锁。 - 该方法会调用
tryReleaseShared(int arg)
尝试更新state
值。由于是共享模式,这里可能涉及到多个线程持有的情况,因此需要确保安全地减少计数,并且适当唤醒后续等待的线程。
- 持有锁的线程调用