常用并发组件源码分析——AQS

53 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

简介

在Java并发体系中,AQS是一个运用非常广泛的工具类,它全称是AbstractQueuedSynchronizer(抽象队列同步器)。从它名字就可以感受到三个含义:

  1. 抽象的

它是一个抽象类,并不能直接使用在我们项目中。

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable 

也正因为它是抽象的,它被灵活应用在各种JUX类上。 image.png

  1. 队列

它借助先进先出 (FIFO) 等待队列的实现相关的阻塞和同步。

  1. 同步器

它是为了实现同步的对象。

关键属性

  1. state 同步器状态,可以理解为当前同步器状态。不同实现类,它具有不同的意义,比如在ReentrantLock中,它可以代表锁的状态。1表示有锁,0表示锁。

相关操作:

protected final int getState() {
    return state;
}
protected final void setState(int newState) {
    state = newState;
}
// 原子操作,通过对象的CAS操作实现。
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
  1. exclusiveOwnerThread 占有当前同步器的线程

  2. head和tail,当前队列的队头元素和队尾元素。

结合实现类ReentrantLock理解AQS

加锁

ReentrantLock,是我们最常用的锁。它底层就是借助了AQS实现加锁和释放锁的过程。

加锁时可以分为两种情况:

  1. 锁未被占用,可以直接拿到。

  2. 锁已被占用,需要等待其他锁释放再获取。

对应ReentrantLock的非公平锁的源码

// 尝试占用税,如果占用成功,说明加锁成功
if (compareAndSetState(0, 1))
    setExclusiveOwnerThread(Thread.currentThread());
else
    acquire(1);

其中compareAndSetState为AQS方法,它通过CAS,保证原子性。

protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

情况1. 通过compareAndSetState(0, 1)方法,直接获得锁

为什么它返回true可以表示获取成功呢?因为它把AQS的state当成锁状态了,state = 0 表示未有线程获得锁;state = 1 表示线程获得锁。compareAndSetState(0, 1)返回true,说明它从未获锁状态,拿到锁了。

情况2. acquire(1),获得锁。

//AQS 方法
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

它的核心流程:尝试获得锁 -》 如果获取锁失败,则把加入到队列中,并将线程状态至为wait -》其他占锁线程,释放锁,并通知等待的节点。

源码比较复杂,总结的流程图如下所示:

未命名文件 (1).png

释放锁

当前拿到的锁线程,释放锁,并唤醒队列的其他节点(线程)

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

流程图如下。

image.png