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
属性和等待队列。