AbstractQueuedSynchronizer

AQS类结构及其说明

本文更近似于手册,依照顺序结构阐明每个接口,实现类及其方法的目的和注意事项。如果在读源码的时候有所疑惑,可以直接查询对应的设计目的。

AQS

提供一个实现阻塞锁和相关同步器的框架(比如,semaphores, events),同步器框架依赖于先进先出(FIFO)等待队列。大多数同步器依赖于单个原子值代表state,子类必须定义改变state的protected方法,并定义这种state对于被获取或释放的对象意味着什么。AQS类的其他方法完成了所有排队和阻塞机制。子类可以维护其他状态字段,但只有使用getState、setState和compareAndSetState方法操作的原子更新的int值才会同步跟踪。

子类应该定义为用于实现其外围类的同步属性的非公共内部助手类。类AbstractQueuedSynchronizer没有实现任何同步接口,相反,它定义了像acquireInterruptible这样的方法,具体的锁和相关的同步器可以适当地调用这些方法来实现它们的公共方法。

AQS可同时支持默认exclusive模式和shared模式或任意一种模式,当以exclusive模式获取时,其他线程尝试获取的请求不能成功,shared模式可由多个线程成功获取。不同模式下等待线程共享同一个先进先出(FIFO)等待队列,通常子类实现类只支持一种模式,但两种模式都可以起作用比如在ReadWriteLock中。

AQS定义了一个嵌套类 AbstractQueuedSynchronizer.ConditionObject ,可作为Condition接口的实现类,支持exclusive模式,isHeldExclusively方法表示了是否同步被当前线程独自持有。使用当前getState值调用的方法release将完全释放该对象,acquire,给定这个保存的状态值,最终将这个对象恢复到它以前获得的状态。AbstractQueuedSynchronizer.ConditionObject的行为取决于同步器实现的语义。

这个类为内部队列提供了检查、检测和监视方法,也为条件对象提供了类似的方法。可以根据需要将它们导出到类中,使用AbstractQueuedSynchronizer作为它们的同步机制。

该类的序列化只存储维持状态的底层原子整数,因此反序列化对象具有空线程队列。需要序列化的典型子类将定义一个readObject方法,该方法在反序列化时将其恢复到已知的初始状态

要使用这个类作为同步器的基础,可通过使用getState, setState和/或compareAndSetState检查和/或修改同步状态来重新定义以下方法

  • tryAcquire
  • tryRelease
  • tryAcquireShared
  • tryReleaseShared
  • isHeldExclusively

默认情况下,这些方法都会抛出UnsupportedOperationException。这些方法的实现必须在内部是线程安全的,并且通常应该简短而不阻塞。定义上述方法是使用该类所支持的唯一方法。所有其他方法都声明为final,因为它们不能独立变化。

您可能还会发现从AbstractOwnableSynchronizer继承的方法对于跟踪拥有独占同步器的线程非常有用。我们鼓励您使用它们——这使得监视和诊断工具能够帮助用户确定哪些线程持有锁。

即使这个类基于内部FIFO队列,它也不会自动执行FIFO获取策略。独占同步的核心采用这种形式

  //Acquire:
    while (!tryAcquire(arg)) {
    	//如果还没入列,将线程入列
        //可能阻塞当前线程
    }
  
  //Release:
    if (tryRelease(arg))
        //解除阻塞第一个排队的线程

因为入队之前调用了在acquire中的检查,所以一个新的获取线程可能会比其他被阻塞和排队的线程抢先。然而,可以定义tryAcquire and/or tryAcquireShared内部执行一个或多个检查方法禁用抢占来实现公平锁。大多数公平锁同步器定义tryAcquire返回false,如果hasQueuedPredecessors方法返回true的话。

虽然这不能保证公平,但允许较早的队列线程在较晚的队列线程之前重新竞争,并且每次重新竞争都有一个无偏的机会在进入的线程中成功。虽然acquire不会在通常意义上“旋转”,但它们可能会在阻塞之前执行对tryAcquire的多次调用,并穿插其他计算。当独占同步只是短暂进行时,这就提供了自旋的大部分好处,而在非独占同步进行时,则不需要承担大部分责任。如果需要,您可以通过前面的“快速路径”检查的acquire方法调用来增强这种功能,可能只在同步器可能不会被争用的情况下预检查hascompeting和/或hasQueuedThreads。

AbstractQueuedSynchronizer.Node

等待队列是“CLH”(Craig, Landin和Hagersten)锁队列的变体。CLH锁通常用于自旋锁。我们使用它们来阻塞同步器,但使用相同的基本策略,即在其节点的前身中保存关于线程的一些控制信息。每个节点中的“状态”字段跟踪线程是否应该阻塞。节点在前驱释放时发信号。否则,队列的每个节点都充当持有单个等待线程的特定通知样式的监视器。status字段并不控制线程是否被授予锁。如果线程是队列中的第一个线程,则可以尝试获取,但不能保证成功;它只给予了竞争的权利。因此,当前释放的竞争者线程可能需要重新等待。

要进入CLH锁队列,作为tail节点加入即可。要退出队列,只需设置头部字段,但节点需要更多的工作来确定谁是其后继者,部分原因是要处理由于超时和中断而可能的取消。prev节点就是主要处理取消,如果一个节点被取消了,它的后续节点(通常)将被重新链接到一个未被取消的前任节点。以解释spin锁的机制。

我们还使用“next”去执行阻塞机制,每个节点的线程id都保存在它自己的节点中,所以前驱通过遍历下一个节点来确定它是哪个线程,从而向下一个节点发出唤醒信号。确定后继节点必须避免与新排队的节点竞争,以设置它们的前驱的“next”字段。当一个节点的后继节点看起来是空的时候,可以通过从原子更新的“tail”向后检查来解决这个问题。(或者换个说法,下一个链接是一种优化,所以我们通常不需要反向扫描。)

消去在基本算法中引入了一些保守性。由于我们必须轮询其他节点的取消,我们可能无法注意到一个被取消的节点是在我们前面还是后面。这个问题的处理方法是在取消后总是取消后继,允许它们稳定在一个新的前驱上,除非我们能确定一个未被取消的前驱来承担这个责任。

CLH队列需要一个哑头节点来启动。但是我们不需要在构造时创造,因为如果没有竞争,那就是白费力气。相反,在第一次竞争时构造节点并设置头和尾指针。

等待条件的线程使用相同的节点,但使用额外的链接。条件只需要链接简单(非并发)链接队列中的节点,因为只有在独占持有时才会访问它们。在等待时,节点被插入到条件队列中。收到信号后,节点被转移到主队列。状态字段的一个特殊值用于标记节点所在的队列。

AQS主要工作

AQS_Exclusive mode

public final void acquire(int arg) {
    if (!tryAcquire(arg) && //功能1:state变量的维护,开发者需要覆写此方法
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//功能2:线程等待队列的维护,AQS覆写
        selfInterrupt();//不响应中断,也就是不抛出异常,目前不在讨论范围,后面会介绍
}

public final boolean release(int arg) {
    if (tryRelease(arg)) { //如果tryRelease返回true,解除一个到多个线程的阻塞
        Node h = head;
        if (h != null && h.waitStatus != 0)
        unparkSuccessor(h);
        return true;
    }
    return false;
}

// 如果lock被当前线程以exclusive方式持有,则返回true。每次调用非等待的AbstractQueuedSynchronizer.ConditionObject方法
// 时都会调用此方法。(等待方法代替调用release)。默认实现抛出UnsupportedOperationException。
// 此方法仅在ConditionObject方法内部调用,因此如果不使用条件,则无需定义此方法。
protected boolean isHeldExclusively() {
    throw new UnsupportedOperationException();
}

// 如果一个节点,总是最初放在condition队列中的节点,现在在同步队列上等待re-acquire,则返回true
final boolean isOnSyncQueue(Node node) {
	if (node.waitStatus == Node.CONDITION || node.prev == null)
		return false;
	if (node.next != null) // If has successor, it must be on queue
		return true;
	/*
     * node.prev can be non-null, but not yet on queue because
     * the CAS to place it on queue can fail. So we have to
     * traverse from tail to make sure it actually made it.  It
     * will always be near the tail in calls to this method, and
     * unless the CAS failed (which is unlikely), it will be
     * there, so we hardly ever traverse much.
     */
    // Node.prev可以是非空的,但还没有在队列中,因为将其放入队列的CAS可能会失败(详见enq()方法)。所以我们必须从尾部遍历以确保 	// 它真的成功了
	return findNodeFromTail(node);
}

private boolean findNodeFromTail(Node node) {
    Node t = tail;
    for (;;) {
        if (t == node)
            return true;
        if (t == null)
            return false;
        t = t.prev;
    }
}

// 将节点从条件队列转移到同步队列。如果成功返回true。如果发信号前节点早已被取消返回false
final boolean transferForSignal(Node node) {
    // 如果不能更改waitStatus,则该节点已被取消
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;
    // 拼接到同步队列上,并尝试设置前驱的waitStatus,以表明线程(可能)正在等待。如果cancelled或尝试设置waitStatus失败,	
    // 则唤醒resync(在这种情况下,waitStatus可以是暂时的,并且无害的错误)。
    Node p = enq(node);
    int ws = p.waitStatus;
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        LockSupport.unpark(node.thread);
    return true;
}

/**
  * 如果有必要,在取消等待后,传输节点到同步队列.
  * 如果在发出信号之前线程已被取消,则返回true。
  */
final boolean transferAfterCancelledWait(Node node) {
    if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
        enq(node);
        return true;
    }
    /*
     * 如果我们丢失了一个signal(),那么我们不能继续,直到它完成它的enq()。在不完全转移期间的中断是罕见的和短暂的,所以只是自   		* 旋。
     */
    while (!isOnSyncQueue(node))
        Thread.yield();
    return false;
}

方法名称 描述
void acquire(int arg) 独占式获取同步状态,如果当前线程获取同步状态成功,则由该方法返回,
否则,将会进入同步队列等待,该方法将会调用重写的tryAcquire(int arg)方法
boolean release(int arg) 独占式释放同步状态,该方法会在释放同步状态后,将同步队列中第一个节点包含的线程唤醒
void acquireShared(int arg) 共享式的获取同步状态,如果当前线程未获取到同步状态,将会进入同步队列等待,
与独占式获取的主要区别是在同一时刻可以有多个线程获取到同步状态。
boolean releaseShared(int arg) 共享式释放同步状态

ReentrantLock

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

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()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

// 如果在当前线程之前有一个排队的线程,则为True;如果当前线程位于队列的头部或队列为空,则为false
public final boolean hasQueuedPredecessors() {
    // The correctness of this depends on head being initialized before tail and on head.next being accurate if the current thread is first in queue.
    Node t = tail; // 以反向初始化顺序读取字段
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

// 为当前线程和给定模式创建并排队节点,初始化node时head是一个哑节点
private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}

// 将节点插入同步队列,必要时进行初始化。为啥?因为队列里有前驱,而condition等待队列是单向队列
private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) { // 必须初始化
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

// 以独占不可中断模式获取已经在队列中的线程。用于条件等待方法和acquire方法
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        // 初始时,假设只有一个在排队,head.waitStatus=0,node.waitStatus=0,而且占有锁的线程也没有释放锁,
        // 就算释放锁了,head.waitStatus=0,也不能唤醒下一个节点,但是,shouldParkAfterFailedAcquire
        // 这个方法将head置为0 -> -1之后会在跑一次循环。后来的排队状态就是-1,-1,...,再看release方法
        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);
    }
}

// 给node的前驱改变状态,waitStatus 0 -> -1
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        /*
         * This node has already set status asking a release
         * to signal it, so it can safely park.
         */
        return true;
    if (ws > 0) {
        /*
         * Predecessor was cancelled. Skip over predecessors and
         * indicate retry.
         */
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        /*
         * waitStatus must be 0 or PROPAGATE.  Indicate that we
         * need a signal, but don't park yet.  Caller will need to
         * retry to make sure it cannot acquire before parking.
         */
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

//中止正在获取的acquire
private void cancelAcquire(Node node) {
    //过滤掉无效节点
    if (node == null)
        return;
    //当前节点线程置为空,为了GC
    node.thread = null;
    //获取当前节点的前一个节点
    Node pred = node.prev;
    //跳过取消的节点
    while (pred.waitStatus > 0)
        node.prev = pred = pred.prev;

    //记录过滤后的节点的后置节点
    Node predNext = pred.next;
    //将当前节点状态改为CANCELLED
    node.waitStatus = Node.CANCELLED;

    // 如果当前节点是tail尾节点 则将从后往前找到第一个非取消状态的节点设为tail尾节点
    if (node == tail && compareAndSetTail(node, pred)) {
        //如果设置成功,则tail节点后面的节点会被设置为null
        compareAndSetNext(pred, predNext, null);
    } else {

        int ws;
        //如果当前节点不是首节点的后置节点
        if (pred != head &&  //并且
            //如果前置节点的状态是SIGNAL
            ((ws = pred.waitStatus) == Node.SIGNAL || //或者
             //状态小于0 并且设置状态为SIGNAL成功
             (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
            //并且前置节点线程不为null时
            pred.thread != null) {
            //记录下当前节点的后置节点
            Node next = node.next;
            //如果后置节点不为空 并且后置节点的状态小于0
            if (next != null && next.waitStatus <= 0)
                //把当前节点的前驱节点的后继指针指向当前节点的后继节点
                compareAndSetNext(pred, predNext, next);
        } else {
            //唤醒当前节点的下一个节点
            unparkSuccessor(node);
        }
        //将当前节点下一节点指向自己
        node.next = node; // help GC
    }
}

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        // 接上面的acquireQueued,exclusive mode情况,如果是公平锁,都是乖乖排队看起来没有意义
        // 如果是非公平锁呢,线程锁释放了,一个新来的抢占了锁完成任务。看到h.waitStatus=0不用唤醒了。然后
        // node=head.next被唤醒,从shouldParkAfterFailedAcquire脱离,抢占锁成功了,node=head.next
        // 状态为-1,没成功head被置为-1
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

private void unparkSuccessor(Node node) {
    // 如果状态是负的(即,可能需要信号),尝试在信号发出之前清除。如果失败或者状态被等待的线程改变,这是可以的
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    //要unpark的线程被保留在继任者中,后者通常只是下一个节点。但如果s.waitStatus=cancelled或为null,则从tail向后遍历,以找     //到实际的non-cancelled的后继
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);
}

protected final boolean isHeldExclusively() {
    // 虽然我们通常必须在读取所有者之前读取状态,但我们不需要这样做来检查当前线程是否为所有者
    return getExclusiveOwnerThread() == Thread.currentThread();
}

acquire()获取一个线程,

  • tryAcquire尝试获取锁
  • 获取锁失败acquireQueued将节点入等待队列,入队后继续获取锁或挂起
    • shouldParkAfterFailedAcquire判断节点前驱的状态决定是否该挂起,node.waitStatus=Node.SIGNAL可以挂起,等于Node.CANCELLED跳过CANCELLED状态的节点,等于0或者-3将节点状态置为Node.SIGNAL,重获取依次
    • parkAndCheckInterrupt阻塞线程,响应中断
    • cancelAcquire将当前节点状态改为cancelld
  • selfInterrupt() 设置中断标志,将中断补上

Condition

Condition将对象监视方法(wait、notify和notifyAll)分解为不同的对象,通过将它们与任意Lock实现结合使用,从而产生每个对象具有多个等待集的效果。Lock代替了同步方法和语句的使用,Condition代替了对象监视器方法的使用。

条件(也称为条件队列或条件变量)提供了一种方法,让一个线程暂停执行(“等待”),直到另一个线程通知某个状态条件现在可能为真。因为对这个共享状态信息的访问发生在不同的线程中,所以必须保护它,因此某种形式的锁与条件相关联。等待条件提供的key属性是,它原子地释放关联的锁并挂起当前线程,就像Object.wait。

一个Condition实例本质上绑定到一个锁。要获取特定Lock实例的Condition实例,请使用它的newCondition()方法。

例如,假设我们有一个支持put和take方法的有界缓冲区。如果在空缓冲区上尝试执行take操作,则线程将阻塞,直到可获得一个item;如果试图在一个已满的缓冲区上执行put操作,那么线程将会阻塞,直到一个space可用为止。我们希望在单独的等待集中坚持等待put线程和take线程。这样我们就可以在缓冲区中item或space可用时只通知单个线程。这可以用两个Condition实例来实现。

class BoundedBuffer {
     final Lock lock = new ReentrantLock();
     final Condition notFull  = lock.newCondition(); 
     final Condition notEmpty = lock.newCondition(); 
  
     final Object[] items = new Object[100];
     int putptr, takeptr, count;
  
     public void put(Object x) throws InterruptedException {
       lock.lock();
       try {
         while (count == items.length)
           notFull.await();
         items[putptr] = x;
         if (++putptr == items.length) putptr = 0;
         ++count;
         notEmpty.signal();
       } finally {
         lock.unlock();
       }
     }
  
     public Object take() throws InterruptedException {
       lock.lock();
       try {
         while (count == 0)
           notEmpty.await();
         Object x = items[takeptr];
         if (++takeptr == items.length) takeptr = 0;
         --count;
         notFull.signal();
         return x;
       } finally {
         lock.unlock();
       }
     }
   }

java.util.concurrent.ArrayBlockingQueue类提供了这个功能,所以没有理由实现这个示例用法类。Condition实现可以提供与Object监视器方法不同的行为和语义,比如保证通知的顺序,或者在执行通知时不需要持有锁。如果实现提供了这样专门的语义,那么实现必须记录这些语义。

注意,Condition实例只是普通对象,它们本身可以用作同步语句中的目标,并且可以调用它们自己的监视等待和通知方法。获取Condition实例的监视器锁,或使用它的监视器方法,与获取与Condition关联的Lock或使用它的waiting和signalling方法没有指定的关系。为了避免混淆,建议您永远不要以这种方式使用Condition实例,除非在它们自己的实现中。

除非注明,否则为任何参数传递null将导致抛出NullPointerException。

实现注意事项

当等待Condition时,通常允许发生“虚假唤醒”,这是对底层平台语义的让步。这对大多数应用程序几乎没有实际影响,因为在循环中应该始终等待Condition,测试正在等待的state谓词。该实现顺带的消除虚假唤醒的可能性,但建议应用程序程序员始终假设它们可以发生,因此始终在循环中等待。

条件等待的三种形式(可中断、不可中断和定时)在某些平台上实现的容易程度和性能特征上可能有所不同。特别是,提供这些特性并维护特定的语义(如排序保证)可能很困难。此外,中断线程的实际挂起的能力在所有平台上不一定都是可行的。

因此,实现不需要为所有三种等待形式定义完全相同的保证或语义,也不需要支持线程的实际挂起的中断。

需要一个实现来清楚地记录每个等待方法提供的语义和保证,当一个实现确实支持线程挂起的中断时,它必须遵守这个接口中定义的中断语义。

由于中断通常意味着取消,而且对中断的检查通常不频繁,因此实现更倾向于响应中断而不是正常的方法返回。即使可以证明中断发生在另一个可能已经解除线程阻塞的操作之后,也是如此。实现应该记录这种行为。

  1. await

    void await() throws InterruptedException

导致当前线程等待,直到收到信号或被中断。

与此Condition关联的Lock被原子的释放,为完成线程调度的目的,当前线程将被禁用,并处于休眠状态,直到发生以下四种情况之一:

  • 其他线程调用此Condition的signal方法,当前线程恰好被选为要唤醒的线程
  • 其他线程调用此Condition的signalAll方法
  • 其他线程中断当前线程,支持中断线程挂起
  • 一个“虚假的唤醒”发生了

在所有情况下,在此方法返回之前,当前线程必须re-acquire与此Condition相关联的Lock。当线程返回时,保证它持有这个锁。

如果当前线程:

  • 在进入此方法时设置了中断状态
  • 在等待时被中断,并且支持中断线程挂起

然后抛出InterruptedException,并清除当前线程的中断状态。在第一种情况下,没有指定是否在释放锁之前进行中断测试。

实现注意事项

当调用此方法时,假定当前线程持有与此Condition关联的Lock。这取决于实现来确定是否为这种情况,如果不是,则如何响应。通常,会抛出一个异常(例如IllegalMonitorStateException),并且实现必须记录这个事实。

一个实现可以更倾向于响应一个interrupt,而不是响应一个signal的正常方法返回。在这种情况下,实现必须确保将signal重定向到另一个等待线程(如果有的话)。

抛出:InterruptedException——如果当前线程被中断(并且支持线程挂起的中断)

  • signal

    void signal();

唤醒一个等待的线程。

如果有任何线程正在等待这个条件,那么就选择一个线程来唤醒它。然后,该线程必须在从await返回之前re-acquire获取Lock。

实现注意事项

当调用此方法时,实现可能(通常也确实)要求当前线程持有与此Condition关联的锁。

实现必须记录这个前提条件以及在未持有锁时所采取的任何操作。通常,会抛出一个异常,如IllegalMonitorStateException。

ConditionObject

作为Lock实现基础的AbstractQueuedSynchronizer的Condition实现类。

这个类的方法文档描述的是机制,而不是Lock和Condition用户的行为规范。这个类的导出版本通常需要附带描述条件语义的文档,这些文档依赖于相关的AbstractQueuedSynchronizer的条件语义。

这个类是可序列化的,但所有字段都是临时的,因此反序列化条件没有waiters。

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

// 添加一个新节点到条件队列
private Node addConditionWaiter() {
    Node t = lastWaiter;
    // 如果lastWaiter处于cancelled状态, 清理掉
    if (t != null && t.waitStatus != Node.CONDITION) {
        unlinkCancelledWaiters();
        t = lastWaiter;
    }
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    if (t == null)
        firstWaiter = node;
    else
        t.nextWaiter = node;
    lastWaiter = node;
    return node;
}

// 遍历整个条件队列,从条件队列中断链所有处于cancelled状态的waiter节点。
private void unlinkCancelledWaiters() {
    Node t = firstWaiter;
    // 保存遍历节点前驱节点的引用
    Node trail = null;
    while (t != null) {
        Node next = t.nextWaiter;
        if (t.waitStatus != Node.CONDITION) {
            t.nextWaiter = null;
            if (trail == null)
                firstWaiter = next;
            else
                trail.nextWaiter = next;
            if (next == null)
                lastWaiter = trail;
        }
        else
            trail = t;
        t = next;
    }
}

// 将等待时间最长的线程,(如果存在),从该Condition的等待队列移动到拥有Lock的等待队列
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;
    } while (!transferForSignal(first) &&
             (first = firstWaiter) != null);
}

// 对于可中断等待,我们需要跟踪是否抛出InterruptedException,如果在条件阻塞时中断,VS,重新中断当前线程,如果中断时阻塞等待
// re-acquire。
// 退出等待时重新中断
private static final int REINTERRUPT =  1;
// 退出等待时抛出InterruptedException
private static final int THROW_IE    = -1;

// 检查中断,如果在发出信号之前中断,返回THROW_IE,如果在发出信号之后中断,返回REINTERRUPT,如果没有中断,返回0
private int checkInterruptWhileWaiting(Node node) {
    return Thread.interrupted() ?
        (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) : 0;
}

// 根据模式的不同,抛出InterruptedException,重新中断当前线程,或者什么也不做。
private void reportInterruptAfterWait(int interruptMode)
    throws InterruptedException {
    if (interruptMode == THROW_IE)
        throw new InterruptedException();
    else if (interruptMode == REINTERRUPT)
        selfInterrupt();
}

ArrayBlockingQueue

由数组支持的有界阻塞队列,该队列以FIFO对元素进行排序,队列的head是队列中存在时间最长的元素,tail是是队列中存在时间最短的元素。在队列尾部插入新元素,队列检索操作获取队列头部的元素。

这是一个经典的“有界缓冲区”,其中一个固定大小的数组保存生产者插入和消费者提取的元素。容量一旦创建,就不能更改。尝试将一个元素放入一个已满的队列中会导致操作阻塞;尝试从空队列中取元素也同样会阻塞。

这个类支持一个可选的公平策略,用于对等待的生产者和消费者线程进行排序。默认情况下,这种排序是不保证的。然而,如果一个队列的公平性设置为true,它将按照FIFO顺序授予线程访问权。公平通常会降低吞吐量,但会减少可变性并避免饿死。

这个类及其迭代器实现了Collection和iterator接口的所有可选方法。

/** The queued items */
final Object[] items;

/** items index for next take, poll, peek or remove */
int takeIndex;

/** items index for next put, offer, or add */
int putIndex;

/** Number of elements in the queue */
int count;

// 并发控制使用经典的双条件算法
/** Main lock guarding all access */
final ReentrantLock lock;

/** Condition for waiting takes */
private final Condition notEmpty;

/** Condition for waiting puts */
private final Condition notFull;

/**
  * Shared state for currently active iterators, or null if there
  * are known not to be any.  Allows queue operations to update
  * iterator state.
  */
transient Itrs itrs = null;


// 在队列的尾部插入指定的元素,如果队列已满,则等待空间可用
public void put(E e) throws InterruptedException {
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length)
            notFull.await();
        enqueue(e);
    } finally {
        lock.unlock();
    }
}

// 实现可中断条件等待
// 1.如果当前线程被中断,抛出InterruptedException
// 2.保存getState返回的锁状态
// 3.以保存的状态作为参数调用release,如果失败则抛出IllegalMonitorStateException
// 4.阻塞直到有信号或中断
// 5.Re-acquire,通过以保存的状态作为参数执行特定版本的acquire方法
// 6.如果在第4步阻塞时中断,则抛出InterruptedException
public final void await() throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    Node node = addConditionWaiter();
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this);
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}

// 使用当前state值调用release;返回保存的state。取消节点并在失败时抛出异常。
final int fullyRelease(Node node) {
    boolean failed = true;
    try {
        int savedState = getState();
        if (release(savedState)) {
            failed = false;
            return savedState;
        } else {
            throw new IllegalMonitorStateException();
        }
    } finally {
        if (failed)
            node.waitStatus = Node.CANCELLED;
    }
}

// 在当前放置位置插入元素,前进并发出信号
private void enqueue(E x) {
    // assert lock.getHoldCount() == 1;
    // assert items[putIndex] == null;
    final Object[] items = this.items;
    items[putIndex] = x;
    if (++putIndex == items.length)
        putIndex = 0;
    count++;
    notEmpty.signal();
}

AQS_shared mode

Semaphore

//semaphore.acquire();
public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}

protected int tryAcquireShared(int acquires) {
    for (;;) {
        if (hasQueuedPredecessors())
            return -1;
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}

private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head; // Record old head for check below
    setHead(node);
    /*
     * Try to signal next queued node if:
     *   Propagation was indicated by caller,
     *     or was recorded (as h.waitStatus either before
     *     or after setHead) by a previous operation
     *     (note: this uses sign-check of waitStatus because
     *      PROPAGATE status may transition to SIGNAL.)
     * and
     *   The next node is waiting in shared mode,
     *     or we don't know, because it appears null
     *
     * The conservatism in both of these checks may cause
     * unnecessary wake-ups, but only when there are multiple
     * racing acquires/releases, so most need signals now or soon
     * anyway.
     */
    // 尝试在以下情况下向下一个队列节点发出信号:传播是由调用者指示的,或者是由前一个操作记录的(在setHead之前或之后的      	// h.waitStatus),和,下一个节点以共享模式等待,或者我们不知道,因为它看起来是空的
    // 这两种检查的保守性可能会导致不必要的唤醒,但只有当有多个竞态条件acquire/releases,所以大多数现在或很快就需要信号
    if (propagate > 0 || h == null || h.waitStatus < 0 ||
        (h = head) == null || h.waitStatus < 0) {
        Node s = node.next;
        if (s == null || s.isShared())
            doReleaseShared();
    }
}

// 共享模式的release动作——发出后续信号并确保传播。(注意:对于独占模式,如果需要信号,release相当于调用head的
// unparkSuccessor)
private void doReleaseShared() {
    /*
     * Ensure that a release propagates, even if there are other
     * in-progress acquires/releases.  This proceeds in the usual
     * way of trying to unparkSuccessor of head if it needs
     * signal. But if it does not, status is set to PROPAGATE to
     * ensure that upon release, propagation continues.
     * Additionally, we must loop in case a new node is added
     * while we are doing this. Also, unlike other uses of
     * unparkSuccessor, we need to know if CAS to reset status
     * fails, if so rechecking.
     */
    // 确保一个发布可以传播,即使有其他正在进行的获取/发布,如果头部需要信号,这将以通常的方式尝试unparkSuccessor。
    // 但是如果没有,则将状态设置为PROPAGATE,以确保一旦release传播继续进行
    // 此外,在执行此操作时,如果添加了新节点,则必须进行循环操作
    // 另外,不像unparkSuccessor的其他用法,我们需要知道CAS重置状态是否失败,如果是这样,需要重新检查。
    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
        }
        // 这边有个bug,或者我没想明白,head的下一个节点被释放,这时的head是刚刚置为head的node
        // 两个问题,怎么release足够的arg值?这样直接退出,head是当时的node,node.next被唤醒去竞争,但head还没变?
        if (h == head)                   // loop if head changed
            break;
    }
}

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

protected final boolean tryReleaseShared(int releases) {
    for (;;) {
        int current = getState();
        int next = current + releases;
        if (next < current) // overflow
            throw new Error("Maximum permit count exceeded");
        if (compareAndSetState(current, next))
            return true;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值