java并发编程 AbstractQueuedSynchronizer(AQS)详解二

文章目录

  • 1 概要
  • 2 ConditionObject结构
  • 3 方法详解
    • 3.1 await()
    • 3.2 signal()
    • 3.3 signalAll()
  • 4 总结

1 概要

当AQS解决了线程同步操作问题之后,但是之前的synchronized里提供了锁的wait() 和 notify()操作。所以AQS也提供了这种机制。使用ConditionObject类。

java 并发编程系列文章目录

2 ConditionObject结构

是一个双向链表,也即提供了一个等待队列,下面会体现作用。

private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;

3 方法详解

3.1 await()

public final void await() throws InterruptedException {
		//当前线程中断位不能是true
	    if (Thread.interrupted())
	        throw new InterruptedException();
	    //添加当前节点 节点状态为condition到ConditionObject等待队列,会剔除不是condition的Node,也就是取消的node,和转移到阻塞队列的节点
	    Node node = addConditionWaiter();
	    //释放当前锁,fullyRelease 就是把state的值全部释放掉,因为可重入的原因,此时会unpark head.next节点的线程,详见详解一
	    int savedState = fullyRelease(node);
	    int interruptMode = 0;
	    //isOnSyncQueue是判断是否在阻塞队列中,在阻塞队列中就不需要阻塞了
	    while (!isOnSyncQueue(node)) {
	    	//阻塞线程
	        LockSupport.park(this);
	        //需要判断唤醒是中断唤醒还是unpark checkInterruptWhileWaiting 如果在进入阻塞队列前中断的是需要抛出异常的,进入阻塞队列后中断的 就重新设置中断标记位
	        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
	            break;
	    }
	    //线程唤醒之后尝试获取锁,因为此时可能多个线程在阻塞 acquireQueued详见详解一
	    //其他的就是对中断处理
	    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
	        interruptMode = REINTERRUPT;
	    if (node.nextWaiter != null) // clean up if cancelled
	        unlinkCancelledWaiters();
	    if (interruptMode != 0)
	        reportInterruptAfterWait(interruptMode);
}

3.2 signal()

相当于notify方法

public final void signal() {
	//只有持有锁的线程才能调用
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
        doSignal(first);
}
private void doSignal(Node first) {
    do {
        if ( (firstWaiter = first.nextWaiter) == null)
            lastWaiter = null;
        first.nextWaiter = null;
        //因为是释放锁,所以当前Node需要进入阻塞队列,唤醒阻塞队列head.next的线程
    } while (!transferForSignal(first) &&
             (first = firstWaiter) != null);
}

final boolean transferForSignal(Node node) {
    //因为入阻塞队列了,所以此时修改状态,啥事情从等待队列断开呢,看await方法
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;

    //进入阻塞队列中
    Node p = enq(node);
    int ws = p.waitStatus;
    //如果当前节点的前一个节点的waitstatus 修改不成-1,如果唤醒当前线程。容错机制。因为需要前一个节点为-1代表后一个节点需唤醒。
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        LockSupport.unpark(node.thread);
    return true;
}

3.3 signalAll()

就是把所有的等待队列里的节点放到阻塞队列中

public final void signalAll() {
   if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
        doSignalAll(first);
}
private void doSignalAll(Node first) {
            lastWaiter = firstWaiter = null;
            do {
                Node next = first.nextWaiter;
                first.nextWaiter = null;
                transferForSignal(first);
                first = next;
            } while (first != null);
        }

4 总结

结合AQS的阻塞队列可以想到(如果没有了解,请看java并发编程 AbstractQueuedSynchronizer(AQS)详解一),抽象出一个等待队列,把同一类的需等待的线程的封装成Node放入等待队列。当可以争抢锁的时候(signal or signalAll)时候放到阻塞队列中去,当释放锁的时候,会有两个情况,一:awiat的阻塞,它也会进入acquireQueued。二:正常的阻塞队列阻塞,和之前逻辑一样。

java并发编程 AbstractQueuedSynchronizer(AQS)详解二_第1张图片

你可能感兴趣的:(java并发编程,java)