为什么你说的AQS,面试官总不满意,看完这篇就明白了


简介
AQS听起来很高大上的样子,实际上它就是AbstractQueuedSynchronizer类的缩写,它是构建锁或者其他同步组件的基础框架。在基于AQS构建的同步器中,只能在一个时刻发生阻塞,从而降低上下文切换的开销,提高了吞吐量。同时在设计AQS时充分考虑了可伸缩行,因此J.U.C中所有基于AQS构建的同步器均可以获得这个优势。

直接讲AbstractQueuedSynchronizer抽象类会让你云里雾里,所以从Lock讲起好理解一些,毕竟大多数人没看过这篇的也会用Lock,我是一个负责人的人,讲原理必讲源码,只有从源码中提取出来的原理才是正确的,其他途径得到的原理都要抱着怀疑的态度。

Lock 接口

public interface Lock

Lock 方法

// 重入锁
void lock();
// 等待过程中,可响应中断(获取锁成功不能响应)
void lockInterruptibly() throws InterruptedException;
// 尝试获取锁,获取成功返回true,否则返回false
boolean tryLock();
// 带等待超时时间的锁
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 解锁
void unlock();
// 条件(一个Lock创建的条件自动绑定改lock)
Condition newCondition();

ReentrantLock 类

public class ReentrantLock implements Lock, java.io.Serializable

如果排除内部类不算,Java中只有ReentrantLock实现了Lock接口。

ReentrantLock 属性

private final Sync sync;

ReentrantLock 只有一个属性,公平锁和非公平锁都是Sync的实现。

ReentrantLock 构造函数

// 默认使用非公平锁
public ReentrantLock() {
    sync = new NonfairSync();
}
// 指定是否使用公平锁
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

ReentrantLock 方法

// 重入锁
public void lock() {
    sync.lock();
}
// 可以响应中断的锁
public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}
// 非阻塞锁
public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}
// 带超时时间非阻塞锁
public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
// 解锁
public void unlock() {
    sync.release(1);
}
// 创建绑定条件
public Condition newCondition() {
    return sync.newCondition();
}
// 查询当前线程保持锁定的个数
public int getHoldCount() {
    return sync.getHoldCount();
}
// 查询当前线程是否保持此锁定
public boolean isHeldByCurrentThread() {
    return sync.isHeldExclusively();
}
// 查询此锁定是否由任意线程保持
public boolean isLocked() {
    return sync.isLocked();
}
// 判断lock锁是公平锁还是非公平锁
public final boolean isFair() {
    return sync instanceof FairSync;
}
// 返回目前拥有此锁的线程
protected Thread getOwner() {
    return sync.getOwner();
}
// 判断是否有处于等待lock的状态
public final boolean hasQueuedThreads() {
    return sync.hasQueuedThreads();
}
// 判断线程是否处于等待lock的状态
public final boolean hasQueuedThread(Thread thread) {
    return sync.isQueued(thread);
}
// 获取正等待获取此锁定线程数
public final int getQueueLength() {
    return sync.getQueueLength();
}
// 获取正等待获取此锁定线程
protected Collection<Thread> getQueuedThreads() {
    return sync.getQueuedThreads();
}
// 查询是否有线程在condition.await状态中
public boolean hasWaiters(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof ConditionObject))
        throw new IllegalArgumentException("not owner");
    return sync.hasWaiters((ConditionObject)condition);
}
// 获取同一个Condition类的等待线程数
public int getWaitQueueLength(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof ConditionObject))
        throw new IllegalArgumentException("not owner");
    return sync.getWaitQueueLength((ConditionObject)condition);
}
// 获取同一个Condition类的等待线程
protected Collection<Thread> getWaitingThreads(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof ConditionObject))
        throw new IllegalArgumentException("not owner");
    return sync.getWaitingThreads((ConditionObject)condition);
}

从ReentrantLock的方法上可以看出所有的锁操作都是基于Sync的实例完成。

ReentrantLock 的 Sync 抽象类

abstract static class Sync extends AbstractQueuedSynchronizer

Sync 抽象类继承AQS

ReentrantLock 的 Sync 方法

// 重入锁
abstract void lock();
// 获取锁(非公平)
final boolean nonfairTryAcquire(int acquires) {
    // 获取当前线程
    final Thread current = Thread.currentThread();
    // 获取锁状态
    int c = getState();
    // 没有线程持有锁
    if (c == 0) {
        // CAS修改锁状态
        if (compareAndSetState(0, acquires)) {
            // 设置当前线程持有锁
            setExclusiveOwnerThread(current);
            // 返回获取成功
            return true;
        }
    }
    // 有线程持有锁时,判断是否为当前线程持有
    else if (current == getExclusiveOwnerThread()) {
        // 锁计数个数
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        // 修改计数个数(状态加1)
        // 持有锁的就是当前线程所以可以直接修改
        setState(nextc);
        return true;
    }
    // 返回获取失败
    return false;
}
// 释放锁
protected final boolean tryRelease(int releases) {
    // 计算计数个数
    int c = getState() - releases;
    // 持有锁的线程是否为当前线程
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    // 是否完全释放锁标志
    boolean free = false;
    // 计数器已经为0,置空持有锁的线程
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    // 修改计数器
    setState(c);
    // 返回最终结果
    return free;
}
// 持有锁的线程是否为当前线程
protected final boolean isHeldExclusively() {
    return getExclusiveOwnerThread() == Thread.currentThread();
}
// 创建条件
final ConditionObject newCondition() {
    return new ConditionObject();
}
// 获取持有锁的线程
final Thread getOwner() {
    return getState() == 0 ? null : getExclusiveOwnerThread();
}
// 获取锁计数个数
final int getHoldCount() {
    return isHeldExclusively() ? getState() : 0;
}
// 是否锁定
final boolean isLocked() {
    return getState() != 0;
}
// 序列化
private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
    s.defaultReadObject();
    setState(0); 
}

ReentrantLock 中Sync 抽象类有两个实现类,NonfairSync非公平锁和FairSync公平锁

ReentrantLock 中FairSync 类

static final class FairSync extends Sync

ReentrantLock 中FairSync 构造函数

FairSync() {
}

ReentrantLock 中FairSync 方法

// 重入锁
final void lock() {
    // 调用AQS中acquire
    acquire(1);
}
// 获取锁
protected final boolean tryAcquire(int acquires) {
    // 获取当前线程
    final Thread current = Thread.currentThread();
    // 获取计数器
    int c = getState();
    // 计数个数为
    if (c == 0) {
        // 如果没有排队获取锁的,就开始获取锁
        if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 如果是当前线程持有锁
    else if (current == getExclusiveOwnerThread()) {
        // 计数个数加1
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        // 返回获取锁成功
        return true;
    }
    // 返回获取锁失败
    return false;
}

ReentrantLock 中NonfairSync 类

static final class NonfairSync extends Sync

ReentrantLock 中NonfairSync 的方法

// 重入锁
final void lock() {
    // 先获取一次锁,获取成功修改锁持有者
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    // 没有获取到锁就去排队
    else
        acquire(1);
}
// 获取锁并返回结果
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

ReentrantLock是平时最常用的锁,正常情况下使用非公平锁。ReentrantLock类大部分人都能看懂,从源码中可以看出它并没有定义state属性、exclusiveOwnerThread属性、acquire方法等,这些其实都是使用AQS的。
AbstractQueuedSynchronizer 抽象类

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer

AbstractQueuedSynchronizer 继承 AbstractOwnableSynchronizer抽象类

AbstractOwnableSynchronizer 抽象类

public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable

AbstractOwnableSynchronizer 抽象类作用就是确定锁归属

AbstractOwnableSynchronizer 属性

private transient Thread exclusiveOwnerThread;

AbstractOwnableSynchronizer 构造函数

protected AbstractOwnableSynchronizer() { }

AbstractOwnableSynchronizer 方法

// 设置锁拥有者
protected final void setExclusiveOwnerThread(Thread thread) {
    exclusiveOwnerThread = thread;
}
// 获取锁拥有者
protected final Thread getExclusiveOwnerThread() {
    return exclusiveOwnerThread;
}

AbstractQueuedSynchronizer 重要内部类 Node

static final class Node

Node 属性

// 共享模式
static final Node SHARED = new Node();
// 独占模式
static final Node EXCLUSIVE = null;
// 状态:取消
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;

Node 构造函数

// 无参构造函数
Node() {
}
// 使用线程和下一个节点构建
Node(Thread thread, Node mode) {
    this.nextWaiter = mode;
    this.thread = thread;
}
// 使用线程和状态构建
Node(Thread thread, int waitStatus) {
    this.waitStatus = waitStatus;
    this.thread = thread;
}

Node 方法

// 是否是共享模式
final boolean isShared() {
    return nextWaiter == SHARED;
}
// 获取上一个节点,若上一个节点为空,抛出异常
final Node predecessor() throws NullPointerException {
    Node p = prev;
    if (p == null)
        throw new NullPointerException();
    else
        return p;
}

AbstractQueuedSynchronizer 属性

// 头节点
private transient volatile Node head;
// 尾节点
private transient volatile Node tail;
// 状态
private volatile int state;
// 内存操作不安全类
private static final Unsafe unsafe = Unsafe.getUnsafe();
// 下面属性都是偏移量
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;

AbstractQueuedSynchronizer 静态代码块

static {
    try {
        stateOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
        headOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
        tailOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
        waitStatusOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("waitStatus"));
        nextOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("next"));
    } catch (Exception ex) { throw new Error(ex); }
}

AbstractQueuedSynchronizer 构造函数

protected AbstractQueuedSynchronizer() { }

AbstractQueuedSynchronizer 基础方法

// 获取锁状态
protected final int getState() {
    return state;
}
// 设置锁状态
protected final void setState(int newState) {
    state = newState;
}
// CAS设置锁状态
protected final boolean compareAndSetState(int expect, int update) {
    // 设置锁状态
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
// 设置头元素
private final boolean compareAndSetHead(Node update) {
        return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
// 设置尾元素
private final boolean compareAndSetTail(Node expect, Node update) {
    return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
// 设置等待状态
private static final boolean compareAndSetWaitStatus(Node node, int expect, int update) {
    return unsafe.compareAndSwapInt(node, waitStatusOffset, expect, update);
}
// 设置下一个元素
private static final boolean compareAndSetNext(Node node,  Node expect, Node update) {
    return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
}

AbstractQueuedSynchronizer 获取独占锁

// 以独占模式获取锁
public final void acquire(int arg) {
    // 获取锁或者等待
    if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        // 只有中断状态,并且获取锁成功才会进来
        selfInterrupt();
}

ReentrantLock.lock()方法中,FairSync公平模式下直接调用acquire(1),NonfairSync非公平模式下先CAS获取一次锁,获取失败也会调用acquire(1)。
acquire方法第一步会调用tryAcquire方法

protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}

在AbstractQueuedSynchronizer抽象类中tryAcquire直接调用是会抛异常的,必须要在子类中覆盖此方法,FairSync、NonfairSync均有tryAcquire方法的实现。仔细看源码的就会发现,非公平模式实际上会使用CAS获取两次锁,第一次lock里面,第二次在这儿。
当tryAcquire获取锁失败时,调用addWaiter方法构建节点

private Node addWaiter(Node mode) {
    // 构建节点
    Node node = new Node(Thread.currentThread(), mode);
    // 尾插法
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        // 设置新的尾节点
        if (compareAndSetTail(pred, node)) {
            // CAS设置成功说明tail指向node
            pred.next = node;
            return node;
        }
    }
    // 能到这儿,说明tail为空或者CAS修改tail失败
    // 调用enq入队(补全双向链表节点关系)
    enq(node);
    // 返回node
    return node;
}

尾节点为空或者CAS修改新尾节点失败时调用enq入队,这有一个问题,如果CAS设置node为尾节点失败,这时node只设置了上节点即node的prev属性,而上级节点的next是不关联node的,也就是说目前这个节点还没入队,但是从这个节点可以向前推到head节点,但是从头节点向后推一定推不到node节点。再来看CAS修改成功pred.next = node 还未执行,这时node已经变成尾节点,node的pred前面已经设置过了,但是上级节点的next还未指向node,这时从head往后找,只能找到原尾节点(oldTail.next还未设置),所以在并发中,要遍历整个链表正确的姿势是从尾节点遍历漏掉节点的概率最低(只设置了prev属性并且CAS失败,这种还未完全入队的就没办法了)。

private Node enq(final Node node) {
    // 自旋
    for (;;) {
        // 尾节点
        Node t = tail;
        // 判断尾节点是否为空
        if (t == null) {
            // 初始化双向链表
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            // 把node追加在tail后
            // addWaiter中已经设置过prev的也会被覆盖
            node.prev = t;
            // 修改node为新的尾节点
            if (compareAndSetTail(t, node)) {
                t.next = node;
                // 这里返回的是原tail,不是node,虽然没用
                return t;
            }
        }
    }
}

在enq方法中先看链表有没有初始化,如果没有初始化就会初始化,同样设置新尾节点时,先设置了prev属性,再使用CAS修改node为新尾节点,跟addWaiter方法入队逻辑一样,只不过这里是自旋一定会入队,一样会有从head向后遍历漏元素的问题。

问题:这里还有一个问题,这里为什么要new一个Node为头节点,为什么不把node设置为tail = head=node?这个问题在解锁的时候回答。

回到acquire方法中,acquire中先获取一次锁,获取失败时使用addWaiter构建node对象,并调用acquireQueued方法

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            // 获取上一个节点
            final Node p = node.predecessor();
            // 上一个节点为头节点时,获取一次锁
            if (p == head && tryAcquire(arg)) {
                // 获取锁成功
                // 设置当前节点为头节点
                setHead(node);
                // 原上级节点的next置空
                p.next = null;
                // 线程获取锁成功标志为取消
                failed = false;
                // 返回中断状态
                return interrupted;
            }
            // 先判断是否可以park,可以就直接park了,否则继续自旋
            if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            // 正常情况下是不会到这的
            // 只有failed = false前面异常才会进来
            cancelAcquire(node);
    }
}

acquireQueued方法中,有且只有p == head && tryAcquire(arg)时才能修改头节点,删除原头节点,并且退出,就算线程被中断,该方法也会忽略中断,但是会返回中断状态。acquireQueued中调用shouldParkAfterFailedAcquire方法,判断当前节点是否可以park

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    // 上级节点状态
    int ws = pred.waitStatus;
    // 上级节点状态为SIGNAL -1时可以park
    if (ws == Node.SIGNAL)
        // 返回可以park
        return true;
    // 上一个节点状态为CANCELLED 1时,重新找上级节点
    if (ws > 0) {
        // 往上找一个状态不是CANCELLED的节点置为上级节点
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        // 设置新上级节点的next
        pred.next = node;
    } else {
        // 状态为int默认值 0、CONDITION -2 、PROPAGATE -3
        // 修改上级节点状态为SIGNAL
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    // 返回不能park
    return false;
}

shouldParkAfterFailedAcquire方法还算好理解,主要就是找上级节点,和重置上级节点状态。重置失败也没关系acquireQueued中自旋还会走进来。如果shouldParkAfterFailedAcquire方法返回可以park,那么调用parkAndCheckInterrupt方法进行park

private final boolean parkAndCheckInterrupt() {
    // 使当前线程park(这一句执行完线程就阻塞了)
    LockSupport.park(this);
    // 清除中断状态,并返回原中断状态
    return Thread.interrupted();
}

parkAndCheckInterrupt方法中调用LockSupport.park,线程就在这等待了,后面代码都不能执行了。但是LockSupport.park是可以响应中断的,当被唤醒或者响应中断后,使用Thread.interrupted()先清楚中断,然后返回原中断状态(在acquire方法中会使用selfInterrupt()继续传递中断状态)
acquireQueued 的finally中,异常是会调用cancelAcquire

private void cancelAcquire(Node node) {
    // 判断node是否为空,为空直接忽略
    if (node == null)
        return;
    // 设置node线程为空
    node.thread = null;
    // 获取上级节点
    Node pred = node.prev;
    // 找一个状态不为取消的上级节点
    while (pred.waitStatus > 0)
        node.prev = pred = pred.prev;
    // 获取pred的下级节点
    Node predNext = pred.next;
    // 设置当前节点状态
    node.waitStatus = Node.CANCELLED;
    // 判断是否为尾,修改尾节点为新找到的上级节点
    if (node == tail && compareAndSetTail(node, pred)) {
        compareAndSetNext(pred, predNext, null);
    } else {
        int ws;
        // 上级节点不是头节点,并且状态为SIGNAL
        if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) {
            // 上级节点的下级执行node的下级
            Node next = node.next;
            if (next != null && next.waitStatus <= 0)
                compareAndSetNext(pred, predNext, next);
        } else {
            // 取消node节点,主要是unpark后续节点(后面说)
            unparkSuccessor(node);
        }
        node.next = node; // 下级节点指向自己
    }
}

AbstractQueuedSynchronizer 释放独占锁

public final boolean release(int arg) {
    // tryRelease在Sync中有实现
    if (tryRelease(arg)) {
        // 获取头节点
        Node h = head;
        // 非公平模式下只有一个线程时,
        // 不需要unparkSuccessor
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

unpark后续节点

private void unparkSuccessor(Node node) {
    // 获取node状态
    int ws = node.waitStatus;
    // 状态小于0
    if (ws < 0)
        // 修改状态为0
        compareAndSetWaitStatus(node, ws, 0);
    // 获取下级节点
    Node s = node.next;
    // 下级节点为空或者状态为取消
    if (s == null || s.waitStatus > 0) {
        s = null;
        // 从尾节点往前找离node最近且不为取消的节点,
        // 为什么不能从head找,前面已经解释过了
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    // 下级节点不为空
    if (s != null)
        // 终止下级节点等待
        LockSupport.unpark(s.thread);
}

细心的可能能发现unparkSuccessor唤醒的是头节点的下一个节点,第一次看的时候这里有一个问题困扰了很久,非公平模式下头节点岂不是会一直在等待?后来想明白了,这种情况不可能出现。
首先在初始化队列时使用的是空Node,A、B两个线程并发,A插队,B入队,此时队列里两个元素空Node和B。A解锁时B被唤醒,B获得锁修改自己为头节点,删除空Node;
如果head释放锁后,A插队,B入队。这时需要注意,释放锁是不会删除head节点,只有在头节点的下一个节点获得锁,才会删除head,这时head跟上面空Node是一个意义了。

问题回答
enq中为什么要new一个Node为头节点,为什么不把node设置为tail = head=node?
如果直接把node设置为头尾,就跟下面unparkSuccessor逻辑冲突了,除非unparkSuccessor中改为唤醒头节点,并且acquireQueued改为获取锁就出队

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

源码猎人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值