AQS
AQS
AQS(AbstractQueuedSynchronizer)是 Java 并发包中的一个抽象类,用于实现构建同步器的基础框架。它是实现锁、信号量和其他同步器的关键组件。AQS
提供了一套底层的同步机制,供开发者基于其进行扩展和实现各种高级同步器。
AQS 的设计思想是使用一个等待队列来管理线程的竞争和等待状态。它维护了一个双向链表的队列,其中的每个节点表示一个等待线程,线程以 FIFO(先进先出)的顺序排队等待。AQS 提供了基于 CAS(Compare and
Swap)操作的方法来管理队列和线程的状态,实现了线程的挂起、唤醒和竞争。
特性
- 状态管理
AQS 维护了一个同步状态(synchronization state),它表示同步器的状态信息。同步状态可以是任意的整数值,并且可以被子类用来表示不同的状态和含义。
- 线程阻塞和唤醒
AQS 提供了线程阻塞和唤醒的机制,以实现线程的等待和恢复。线程在无法获取同步资源时可以被阻塞,直到其他线程释放资源并唤醒它们。
- 等待队列
AQS 使用一个等待队列(wait queue)来管理等待获取同步资源的线程。等待队列是一个双向链表,线程以 FIFO(先进先出)的顺序排队等待。通过等待队列,AQS 实现了公平性和线程的顺序保证。
- 独占与共享模式
AQS 支持独占模式和共享模式的同步器。独占模式是指一次只能有一个线程获取同步资源,而共享模式是指多个线程可以同时获取同步资源。
- 可重入性
AQS 支持同一个线程多次获取同步资源,即可重入(Reentrant)特性。同一个线程可以多次获取同步资源,而不会造成死锁或其他并发问题。
- 条件变量支持
AQS 提供了 Condition 对象,用于支持条件变量的功能。条件变量允许线程在满足特定条件之前等待,并在条件满足时被唤醒。
核心结构
同步状态(Synchronization State)
AQS 内部维护了一个同步状态(synchronization state),它是一个整数值,表示同步器的状态信息。同步状态可以用来表示不同的状态和含义,具体取决于具体的同步器实现。
等待队列(Wait Queue)
AQS 使用一个等待队列来管理等待获取同步资源的线程。等待队列是一个双向链表,每个节点表示一个等待线程。等待队列的设计保证了线程的先进先出(FIFO)顺序。
Node 对象
等待队列中的每个节点都由 Node 对象表示,它包含了线程等待状态、线程引用以及等待条件等信息。Node 对象的设计和状态变化对于实现同步的正确性和性能至关重要。
CAS 操作
AQS 使用 CAS(Compare and Swap)操作来实现同步状态的原子性操作。通过 CAS,可以确保同步状态的更新是原子的,避免多个线程同时修改同一状态造成的竞态条件。
共享模式和独占模式
AQS 支持独占模式和共享模式的同步器。独占模式是指一次只能有一个线程获取同步资源,而共享模式是指多个线程可以同时获取同步资源。
条件变量支持
AQS 提供了 Condition 对象,用于支持条件变量的功能。条件变量允许线程在满足特定条件之前等待,并在条件满足时被唤醒。
方法
AQS 的主要方法包括:
- acquire(int arg):获取同步状态,如果获取不到则进入等待状态。
- release(int arg):释放同步状态,并唤醒等待队列中的下一个线程。
- tryAcquire(int arg):尝试获取同步状态,如果成功则返回 true,否则返回 false。
- tryRelease(int arg):尝试释放同步状态,如果成功则返回 true,否则返回 false。
- isHeldExclusively():判断当前线程是否独占持有同步状态。
访问方式
Exclusive(独占)
独占模式(Exclusive)是指在同一时刻只允许一个线程持有锁或访问临界区,其他线程必须等待锁的释放才能继续执行。
通过 AQS 的独占模式,多个线程可以竞争同一个锁,但只有一个线程可以持有锁进行临界区的访问,从而实现线程间的互斥和同步。许多基于 AQS 的同步器和锁,如ReentrantLock就是基于 AQS
的独占模式实现的。开发者可以通过继承 AQS 并实现相关的抽象方法来构建自定义的独占模式同步器。
实现思路
- 内部维护一个同步状态变量(state),用于表示锁的状态。在独占锁模式下,state 的值通常用于表示锁的持有状态,例如 0 表示未锁定状态,1 表示锁定状态。
- 获取锁
- 当一个线程尝试获取独占锁时,它会调用 AQS 的 acquire() 方法。
- 在 acquire() 方法中,线程首先会尝试通过 CAS(Compare and Swap)操作将 state 从 0 更新为 1,以尝试获取锁。
- 如果 CAS 操作成功,表示锁获取成功,线程可以继续执行临界区的代码。
- 如果 CAS 操作失败,表示锁已被其他线程持有,当前线程将被加入到等待队列尾部,进入等待状态。
- 释放锁
- 当持有锁的线程需要释放锁时,它会调用 AQS 的 release() 方法。
- 在 release() 方法中,线程首先会将 state 的值设置为 0,表示锁已释放,然后,线程会检查等待队列中是否有等待的线程,如果有,则选择一个线程唤醒,使其能够继续尝试获取锁。
Share(共享)
共享模式(Shared Mode)用于实现共享锁的功能。其允许多个线程同时获取同一个锁或访问临界区,以实现并发访问共享资源的能力。
通过 AQS 的共享模式,多个线程可以同时获取同一个共享锁,进入临界区执行共享资源的访问。这种模式适用于一些读多写少的场景,允许多个线程同时读取共享资源,而在写操作时需要独占访问。基于 AQS 的共享锁,如
Semaphore或者CountDownLatch,提供了更高的并发性能,允许多个线程同时读取数据,提高系统的吞吐量。
实现思路
- 内部维护一个同步状态变量(state),用于表示锁的状态。在共享模式下,state 的值通常用于表示当前锁被多少个线程持有。
- 获取锁
- 当一个线程尝试获取共享锁时,它会调用 AQS 的 acquireShared() 方法。
- 在 acquireShared() 方法中,线程会根据当前的同步状态(state)和其他线程的状态,决定是否能够获取共享锁。
- 如果可以获取共享锁,则线程可以继续执行临界区的代码。
- 如果无法获取共享锁,线程将进入等待状态,并加入等待队列。
- 释放锁
- 当持有共享锁的线程需要释放锁时,它会调用 AQS 的 releaseShared() 方法。
- 在 releaseShared() 方法中,线程首先会更新同步状态(state)来释放共享锁,然后,线程会通知等待队列中的其他线程,让它们有机会竞争获取共享锁。
队列
Synchronization Wait Queue(同步等待队列)
同步等待队列是 AQS 中用于管理等待获取锁的线程的队列。当一个线程无法获取锁而需要等待时,它会被加入到同步等待队列中。同步等待队列是一个双向链表,由 Node 对象表示,每个节点对应一个等待线程。
Condition Wait Queue(条件等待队列)
条件等待队列是 AQS 中用于管理等待特定条件的线程的队列。AQS 提供了 Condition 对象来支持条件变量的功能。当一个线程在某个条件上等待时,它会被加入到条件等待队列中。条件等待队列是一个单向链表,由 Node
对象表示,每个节点对应一个等待线程。
区别
这两种队列的区别在于它们所管理的线程的目的和等待条件不同:
- 同步等待队列用于管理等待获取锁的线程,这些线程都是在同步操作中等待锁的释放。
- 条件等待队列用于管理等待特定条件的线程,这些线程等待条件满足才能继续执行。
节点状态
- 初始值
值为0,表示当前节点在sync队列中,等待着获取锁。
- CANCELLED
值为1,表示节点被取消。当一个节点的线程被中断或超时等情况下,节点可能会被取消。
- SIGNAL
值为-1,表示后继节点被阻塞。当一个节点的线程需要释放锁或满足某个条件时,它会唤醒其后继节点。
- CONDITION
值为-2,表示节点在条件队列中等待。当一个线程调用了 Condition 的 await() 方法后,它会被移动到条件队列中,并处于等待状态。
- PROPAGATE
值为-3,表示共享模式传播。用于共享模式同步器中,表示当前线程需要唤醒其后继节点。