AbstractQueuedSynchronizer(AQS)
概述
AbstractQueuedSynchronizer(简称AQS)是Java中锁和同步器的基础框架,大多数的锁和同步器都是通过继承AQS来实现的。AQS提供了一个灵活的框架,使得开发者可以根据自己的需求来实现各种锁和同步器。本文将介绍AQS的基本原理和实现细节。
基本原理
AQS维护了一个state(状态)属性和一个等待队列。state用来表示同步状态,当state为0时表示锁没有被占用,当state不为0时表示锁已经被占用,state的含义和具体使用方法是由子类定义的。等待队列则用来存储那些请求锁或者同步器的线程。
当一个线程请求锁或同步器时,如果此时state为0,则表示当前线程可以获得锁或同步器,此时会将state设置为1,表示当前锁或同步器已经被占用了。如果此时state不为0,则表示此时锁或同步器正在被其他线程占用,此时当前线程会被加入到等待队列中,等待其他线程释放锁或同步器。
当其他线程释放了锁或同步器时,会唤醒等待队列中的一个线程,使其重新尝试获取锁或同步器。如果此时该线程又成功获取了锁或同步器,则该线程会从等待队列中移除。
实现细节
等待队列
AQS的等待队列是一个双向链表,每个节点表示一个等待线程(或者称为Node),Node继承自AbstractQueuedSynchronizer。下面是Node的定义:
static final class Node { // 共享模式 static final Node SHARED = new Node(); // 独占模式 static final Node EXCLUSIVE = null; // waitStatus的值表示当前节点需要等待的状态 static final int CANCELLED = 1; static final int SIGNAL = -1; static final int CONDITION = -2; static final int PROPAGATE = -3; volatile int waitStatus; volatile Node prev; volatile Node next; volatile Thread thread; Node nextWaiter; final boolean isShared() { return nextWaiter == SHARED; } final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; } Node() { } Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; this.thread = thread; } Node(Thread thread, int waitStatus) { // Used by Condition this.waitStatus = waitStatus; this.thread = thread; } }
Node节点有以下属性:
waitStatus:表示节点的状态,可以是以下值:
CANCELLED:表示节点已经取消。SIGNAL:表示在释放同步器或者锁时需要唤醒该节点。CONDITION:表示节点在等待Condition时被阻塞。PROPAGATE:表示共享模式下下一个节点需要唤醒。
prev:表示当前节点的前一个节点。next:表示当前节点的后一个节点。thread:表示当前节点所对应的线程。nextWaiter:表示下一个等待节点。
等待队列的头节点是一个哑节点,不表示任何一个等待线程。当等待队列不为空时,队首节点表示正在占用同步器或者锁的线程,队列中剩下的节点表示正在等待获取同步器或者锁的线程。
等待队列的操作主要有以下几个:
enq(node):将一个节点加入等待队列的尾部。addWaiter(mode):创建一个新的节点并加入到等待队列的尾部。mode表示该节点的模式,可以是独占模式或共享模式。setHead(node):设置等待队列的头节点。unparkSuccessor(node):唤醒等待队列中最近的一个还未被取消的节点。在独占模式下,需要唤醒的节点是当前节点的后继节点;在共享模式下,需要唤醒的节点是等待队列中第一个未被取消的节点。
state属性
在AbstractQueuedSynchronizer中,state属性是一个volatile类型的整数,用来表示同步状态。子类可以通过修改state的值来实现自己的同步机制。
当state为0时表示同步器或锁没有被占用,当state不为0时表示同步器或锁已经被占用。在独占模式下,state的值为1表示锁已经被占用,在共享模式下,state的值表示正在使用锁的线程数。
state的主要操作有以下几个:
getState():获取当前状态的值。setState(newState):设置当前状态的值。compareAndSetState(expect, update):原子性地将当前状态和expect比较,如果相等则将其设置为update,并返回true,否则返回false。
独占模式
AbstractQueuedSynchronizer中的独占模式是最常用的同步机制之一,代表了Java中的锁。
我们来看一下ReentrantLock是如何实现独占模式的。
ReentrantLock
ReentrantLock是一个可重入的互斥锁,它支持公平锁和非公平锁。ReentrantLock继承自AbstractQueuedSynchronizer,在ReentrantLock的构造方法中会调用AbstractQueuedSynchronizer的构造方法来初始化state属性和等待队列。