队列同步器 AbstractQueuedSynchronizer,是用来构建锁或者其他同步组件的基础框架,它使用了一个int成员变量表示同步状态,通过内置的FIFO 队列来完成资源获取线程的排队工作。
Java SE 5 之后,并发包中新增了 Lock 接口(以及相关实现类),用来实现锁功能,它提供了与 synchronized 关键字类似的同步功能,只是在使用时需要显式地获取锁和释放锁。虽然它缺少了隐式获取释放锁的便捷性,但是却拥有了锁获取与同步的可操作性,可中断的获取锁以及超时获取锁等多种synchronized 关键字不具备的功能
Lock 接口提供的synchronized 关键字不具备的主要特性
AQS的设计是基于模板方法模式的,也就是说,使用者需要继承同步器并重写指定的方法,随后将同步器组合在自定义同步组件的实现中,并调用同步器提供的模板方法,而这些模板方法将会调用使用者重写的方法,这样就大大降低了实现一个可靠自定义同步组件的门槛
package lockTest;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* @Description: AQS
* @Author: Aiguodala
* @CreateDate: 2021/4/19 10:51
*/
public class AQSTest {
private static final MyLock LOCK = new MyLock();
public static void main(String[] args) {
new Thread(() -> {
LOCK.lock();
try {
System.out.println("第一个线程获取锁");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("第一个线程释放锁");
LOCK.unlock();
}
}).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
LOCK.lock();
try {
System.out.println("第二个线程获取锁");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("第二个线程释放锁");
LOCK.unlock();
}
}).start();
}
}
class MyLock implements Lock {
/**
* 静态内部类,自定义同步器
*/
private static class Sync extends AbstractQueuedSynchronizer {
/**
* 是否处于占用状态
* @return
*/
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
/**
* 当状态为 0 的时候获取锁
* @param arg
* @return
*/
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0,1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
/**
* 释放锁,将状态置为 0
* @param arg
* @return
*/
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0) {
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
// setState 放在最后是因为 state 变量是volatile 修饰的,加入写屏障可以保证可见性
setState(0);
return true;
}
/**
* 每个Condition 都包含了一个Condition队列
* @return
*/
Condition newCondition () {
return new ConditionObject();
}
}
/*********** 以下方法只需要将操作代理到 Sync 对象上 ***********/
/**
* 创建锁对象
*/
private final Sync sync = new Sync();
@Override
public void lock() {
sync.acquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1,unit.toNanos(time));
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
}
同步器依赖内部的同步队列来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态的等信息构造成一个节点( Node )并将其加入到同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态。
Node 主要字段
/**
* 等待状态
*/
volatile int waitStatus;
/**
* 前驱节点,当节点被加入到等待队列时被添加
*/
volatile Node prev;
/**
* 后继节点
*/
volatile Node next;
/**
* 获取同步状态的线程
*/
volatile Thread thread;
/**
* 等待队列的后继节点,如果当前节点是共享的,那么这个字段将是一个SHARED常量,
* 也就是说节点类型(独占或者共享)和等待队列中的后继节点共用一个字段
*/
Node nextWaiter;
/**
* 由于在同步队列中等待的线程等待超时或者被中断,需要从同步队列中取消等待
* 节点变成该状态将不会变化
*/
static final int CANCELLED = 1;
/**
* 后继节点的线程处于等待状态,而当前节点的线程如果释放了同步状态或者被取消
* 将会通知后继节点,使得后继节点得以运行
*/
static final int SIGNAL = -1;
/**
* 节点在等待队列中,节点线程等待在Condition上
* 当其他线程对Condition调用了signal() 方法后,该节点会从等待队列转移到同步队列中
*/
static final int CONDITION = -2;
/**
* 表示下一次共享式同步状态会被无条件传播下去
*/
static final int PROPAGATE = -3;
队列结构
获取同步状态,该方法对中断不敏感,也就是由于线程获取同步状态失败后进入同步队列中,后续对线程进行中断操作时,线程不会从同步队列中移除
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
addWaiter (Node node)
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// 备份tail节点引用
Node pred = tail;
if (pred != null) {
node.prev = pred;
// 利用CAS 设置尾结点
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 不成功进入enq
enq(node);
return node;
}
enq(node)
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) {
// Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
// 只有通过CAS成功添加节点才会退出
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
// 死循环,不断自旋,获取同步状态才可以退出
for (;;) {
// 获取node 的前驱节点
final Node p = node.predecessor();
// 只有前驱节点是头结点,才能尝试获取同步状态
if (p == head && tryAcquire(arg)) {
// 将当前节点设置为头结点
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// shouldParkAfterFailedAcquire 将前驱节点状态置为 -1 用于标记谁来唤醒该被阻塞地节点
// 如果 shouldParkAfterFailedAcquire 返回true 则进入parkAndCheckInterrupt 节点被阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
当前线程获取同步状态并执行相应逻辑之后,需要释放同步状态,使得后续节点能够继续获取同步状态。
public final boolean release(int arg) {
// 调用自己重写的tryRelease(arg) 方法
if (tryRelease(arg)) {
Node h = head;
// 唤醒处于等待的线程
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
共享式获取与独占式获取最重要的区别在于同一时刻是否能有多个线程同时获取到同步状态。
例如:读文件是共享式,而写文件是独占式
public final void acquireShared(int arg) {
// 调用自定义的tryAcquireShared(arg) 方法,返回值大于0 则获取同步状态
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
private void doAcquireShared(int arg) {
// 构造SHARED 共享式节点
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
// 自旋
for (;;) {
// 获得前驱节点
final Node p = node.predecessor();
// 如果前驱节点是头结点,则尝试获取同步状态
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
public final boolean releaseShared(int arg) {
// 调用自己重写的tryReleaseShared(arg) 方法尝试释放同步状态
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
// 必须确保同步状态的线程安全释放,因为释放同步操作的线程
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
通过调用同步器的doAcquireNanos(int arg, long nanosTimeout) 方法,可以超时获取同步状态,提供了synchronized所不具备的特性
实现逻辑和独占式获取同步状态大体相同,只是在获取失败时会判断是否超时
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return true;
}
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}