Java-第十六部分-JUC-AQS

189 阅读7分钟

JUC全文

AQS

  • JUC包的核心,Abstract Queued Synchronizer,抽象队列同步器,底层是双向队列,ReentrantLock的底层
  • AQS队列特点
  1. 队列中的第一个节点,是占有锁的线程对应的节点
  2. 初始化队列时,需要初始化两个节点
  • CLH算法,自旋锁算法,充分运用CPU
  • 六个步骤
  1. 抢锁
  2. 释放锁
  3. 入队
  4. 出队
  5. 阻塞
  6. 唤醒

waitStatus

0 默认
//取消
static final int CANCELLED =  1;
//闹钟,需要被唤醒,可挂起
static final int SIGNAL    = -1;
// Condition时的状态
static final int CONDITION = -2;
// 共享模式下,countDownLatch下的标记状态
static final int PROPAGATE = -3;

抢锁

  • tryAcquire抢锁
  • 公平锁和非公平锁存在差异的地方,占有锁的线程已经执行完任务,释放了锁,将状态位恢复,这时恰好来了一个线程的时候,如果非公平锁没有争抢到,那么也像公平锁一样排队
  • 非公平锁 image.png
  • 看锁标志位state,默认是0,没有线程占用锁,线程可以占有锁,改成1,需要一种机制(AQS),保证同一时刻,只能有一个线程能改state
  • 此时state为0,那么可以抢锁 image.png
  • 有线程占有锁,这时有线程来抢锁,需要判断
  1. 当前来抢锁的线程是否为占有锁的线程,如果是,则重入(双层锁),state+1,释放时需要释放对应的次数
  2. 当前来抢锁的线程不是占有锁的线程,抢锁失败
  • 优化,看等待区域是否有其他线程,如果有,该锁已经被占用
  • hasQueuedPredecessors看等待区是否有其他线程等待(等待区域通过链表连接),如果没有,再试着调用compareAndSetState设置标志位 image.png
  • hasQueuedPredecessors
public final boolean hasQueuedPredecessors() {

    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    // true的情况,说明有线程排队 head != tail 且 头节点后有线程,但是该线程不为当前线程 
    // (s = h.next) == null 是为了防止空指针,s == null 为真 就不会继续判断
    // false的情况 head == tail(没有其他线程线程排队) 或 s.thread 为当前线程,当前线程已经排第二个了
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}
  • 没有其他线程等待,且设置成功,将当前线程进行设置 image.png
  • 如果state不为0,那么就是重入逻辑,检查当前线程是否为占有锁的线程 image.png

入队

  • 没有抢到锁tryAcquire返回false,那么尝试入队 image.png
  • addWaiter image.png
  • 队列中有无节点,如果没有,需要自旋初始化,进入enq方法,生成两个节点
private Node enq(final Node node) {
    //第一次时进行了两次循环
    //并且自旋放入
    for (;;) {
        //第一次tail为null
        Node t = tail;
        if (t == null) { // Must initialize
            //生成一个空节点设置为head和tail
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            //第二次循环,将新加入的node.prev指向head/tail
            //非初始化时,新入队的节点,指向前一个节点,并将tail设置为新加入的节点,
            //交换完后,此时t为前一个节点
            node.prev = t;
            //将tail设置为node
            if (compareAndSetTail(t, node)) {
                //此时t仅为head,将head.next设置为node
                t.next = node;
                return t;
            }
        }
    }
}
  • 如果有,尾部插入
private Node addWaiter(Node mode) {
    //包装成node
    Node node = new Node(Thread.currentThread(), mode);
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        //将新node设置为tail
        if (compareAndSetTail(pred, node)) {
            //前一个node指向新node(也就是tail)
            pred.next = node;
            return node;
        }
    }
    //如果直接插入失败,也需要自旋处理
    enq(node);
    return node;
}
  • 入队完成后,尝试自旋抢锁
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);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            //如果该线程抢锁失败尝试阻塞
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt()) //阻塞
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
  • shouldParkAfterFailedAcquire
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL) //-1
        return true;
    if (ws > 0) { //前面的节点可以被取消
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else { //对于最开始来说,head.waitStatus为0,那么将其设置为-1,也就是上闹钟
        //第二次进入时,则能返回true
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

释放锁

  • unlock
  • 解决重入问题锁状态位恢复为0唤醒其他线程
  • tryRelease,改变state,当state==0时,返回true
protected final boolean tryRelease(int releases) {
    int c = getState() - releases; //state - 1
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

唤醒

  • release,当tryRelease解决重入后,唤醒线程
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        // 要保证头节点为0,并唤醒后面的节点
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
  • unparkSuccessor
private void unparkSuccessor(Node node) { 
    int ws = node.waitStatus;
    //某些信号量会导致waitStatus<0
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    Node s = node.next;
    //s == null 防止空指针,s是被取消的节点,找到非被取消的s节点唤醒
    if (s == null || s.waitStatus > 0) {
        s = null;
        //从尾部开始
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                //找到node的下一个waitStatus<=0的睡眠节点
                s = t;
    }
    if (s != null)
        //唤醒
        LockSupport.unpark(s.thread);
}

出队

  • 由被唤醒的线程来做
  • 当进入parkAndCheckInterrupt阻塞后,被唤醒,将再次进入这个线程,尝试抢锁
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设置为null,此时p节点没有被指向或者指向另一个node
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt()) //阻塞
                interrupted = true;
        }
    } finally {
        if (failed) 
            //当期间有异常发生,将该node.waitStatus置为Node.CANCELLED - 1
            //将当前节点删除
            cancelAcquire(node);
    }
}
  • setHead
private void setHead(Node node) {
    head = node;
    node.thread = null;
    node.prev = null;
}
  • cancelAcquire处理唤醒失败,将该node.waitStatus置为Node.CANCELLED - 1,将当前节点删除,删除头节点会由下一个的节点删除
private void cancelAcquire(Node node) {

    if (node == null)
        return;

    node.thread = null;
    //找到当前节点的最前面没被取消的节点
    Node pred = node.prev;
    while (pred.waitStatus > 0)
        node.prev = pred = pred.prev;
    //取当前父节点的下一个节点
    Node predNext = pred.next;
    //将当前节点设置为可取消的
    node.waitStatus = Node.CANCELLED;

    //当前节点为tail节点,直接将当前父节点设置为tail,因为prevNext是cancel
    //如果node.prev 就是pred,那么也可以直接这么操作,此时predNext就是node
    if (node == tail && compareAndSetTail(node, pred)) {
        compareAndSetNext(pred, predNext, null);
    } else {
        int ws;
        //不是头节点 且 状态为-1,待唤醒 或 (ws < 0 且 设置为-1成功)
        if (pred != head &&
            ((ws = pred.waitStatus) == Node.SIGNAL ||
             (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
            pred.thread != null) {
            //将node的next,连接上pred,相当于从中间删除node
            Node next = node.next;
            if (next != null && next.waitStatus <= 0)
                compareAndSetNext(pred, predNext, next);
        } else {
            //如果是头节点 再次尝试唤醒node,因为已经没有别的节点了
            unparkSuccessor(node);
        }
        //node指向自己,让gc回收
        node.next = node; // help GC
    }
}

Condition

  • AQS的Node节点中有nextWaiter属性,用于连接await()的等待节点

不带头节点的队列

await

  • 创建节点插入等待队列
  • 获取当前同步队列的状态,并释放锁

只有获得了lock的线程,才能进行await,否则会抛IllegalMonitorStateException

  • 循环检测是否被加入同步队列,也就是苏醒,开始抢锁

响应中断

  • await()
public final void await() throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    // 包装成Node,尾插入等待队列
    Node node = addConditionWaiter();
    // 释放当前线程占用的lock,并唤醒下一个节点
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    // 当前是否被移动到AQS同步队列,因为await的主要目标就是挂起,直到被signal
    // 最开始加入的是等待队列
    while (!isOnSyncQueue(node)) {
        // 挂起当前线程
        LockSupport.park(this);
        // 中断后,会跳出循环
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    // 自旋等待获取到lock
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    // 处理中断情况
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}
  • addConditionWaiter()加入等待队列
private Node addConditionWaiter() {
    Node t = lastWaiter;
    // If lastWaiter is cancelled, clean out.
    if (t != null && t.waitStatus != Node.CONDITION) {
        unlinkCancelledWaiters();
        t = lastWaiter;
    }
    // 包装成Node 设置状态为 Node.CONDITION
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    // 插入尾
    if (t == null)
        firstWaiter = node;
    else
        t.nextWaiter = node;
    // 更新尾队列
    lastWaiter = node;
    return node;
}
  • fullyRelease当前线程释放锁,说明如果不是上锁的lock,进行await,会抛异常
final int fullyRelease(Node node) {
    boolean failed = true;
    try {
        // 此时应该是1
        int savedState = getState();
        // 释放资源
        if (release(savedState)) {
            failed = false;
            return savedState;
        } else {
            throw new IllegalMonitorStateException();
        }
    } finally {
        if (failed)
            node.waitStatus = Node.CANCELLED;
    }
}
  • isOnSyncQueue判断是否移动到了同步队列
final boolean isOnSyncQueue(Node node) {
    // 检查状态
    if (node.waitStatus == Node.CONDITION || node.prev == null)
        return false;
    // 如果状态是0,且是一个后继节点,那么肯定加入了同步队列,可以唤醒
    if (node.next != null) // If has successor, it must be on queue
        return true;
    // 从同步队列尾部开始找,如果找到了就在
    return findNodeFromTail(node);
}
  • awaitNanos在循环期间,对进行判断,超时后,设置节点为状态并break

signal

  • signal
public final void signal() {
    // 检测当前线程是否获得lock
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
        //唤醒等待队列中的第一个
        doSignal(first);
}
  • doSignal唤醒操作
private void doSignal(Node first) {
    do {
        // firstWaiter 往后移动,并检查等待队列后面是否有节点
        if ( (firstWaiter = first.nextWaiter) == null)
            lastWaiter = null;
        first.nextWaiter = null;
    } while (!transferForSignal(first) &&
             (first = firstWaiter) != null);
}
  • transferForSignal移动到同步队列,等待抢锁
final boolean transferForSignal(Node node) {
   // 更新状态为0
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;
    // 加入同步队列
    Node p = enq(node);
    int ws = p.waitStatus;
    // 唤醒当前线程,将该节点设置为可挂起-1
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        LockSupport.unpark(node.thread);
    return true;
}
  • signalAll
public final void signalAll() {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
        doSignalAll(first);
}
  • doSignalAll从第一个开始,移动全部的,加入同步队列,并唤醒
private void doSignalAll(Node first) {
    lastWaiter = firstWaiter = null;
    do {
        Node next = first.nextWaiter;
        first.nextWaiter = null;
        transferForSignal(first);
        first = next;
    } while (first != null);
}