AQS 框架简介
AQS(Abstract Queued Synchronizer)抽象队列同步框架。
比如,ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch、ThreadPoolExcutor(JDK 1.8),这些类的底层原理都是AQS(工具类)。
详情可查看:JAVA 线程池ThreadPoolExecutor及核心参数
作用:使用AQS 可以极大的减少上层代码量,避免重复造轮子,同时避免处线程安全的问题。
对于ReentrantLock、ThreadPoolExcutor等线程协作工具类而言,如果能把这些类的类似工作的代码给提取出来,变成一个底层工具类的话,就可以重复使用这个工具类来构建上层代码了,而这个工具类其实就是 AQS。
功能:线程的原子性管理、线程的阻塞与解除阻塞、队列的管理。
AQS(AbstractQueuedSynchronizer)的原理
基于一个FIFO(先进先出)队列 和 一个同步状态(state)来实现线程的排队和同步。
状态 State
private volatile int state;
于ReentrantLock而言,state 用来表示锁的占有情况。(因可重锁,state是>=0的正整数)。
state会被多个线程共享并发修改,所以state的定义中加了 volatile 修饰。Java 并发编程之volatile可见性,原子操作线程不安全
显然,是不足以保证线程安全的。
AQS通过CAS 操作,利用 CPU 指令的原子性保证了操作的原子性+可见性,从而保证线程安全。
Java CAS原子操作过程及ABA问题
FIFO 队列
AQS维护一个FIFO队列,用于存储等待获取同步状态的线程。
由于是FIFO(先进先出)保证了线程队列的有序性、公平性。
队列是双向链表的结构,每个线程通过Node对象来表示,并包含了线程的引用和状态信息。head头节点为“当前持有锁的线程”,后面的线程则要被阻塞并等待唤醒。
线程调度
AQS通过 CAS 自旋(自旋锁)和阻塞(等待队列)的方式来进行线程的调度。
当线程无法获取同步状态时,会进入阻塞状态,直到被前面的节点唤醒。在自旋期间,线程会不断尝试获取同步状态,避免线程切换的开销。
AQS 机制
当线程尝试获取同步状态时,如果同步状态已经被其他线程占用,则将当前线程包装成一个节点(Node)并入队等待,否则直接获取同步状态。同时,AQS会确保只有队列中第一个节点的线程才能获取同步状态,其他节点需要等待前面的节点释放同步状态。
例如,对于ReentrantLock而言,在一个线程中调用它的 lock() 执行时。会先判断如果 state 值不等于 0 ,且当前线程不是持有锁的线程,即非重入锁。那么这个锁就已经被其他线程所持有了。这个时候,当前线程就获取不到锁,于是就让该线程进入阻塞队列中。