AQS:AbstractQueuedSynchronizer(队列同步器)

AQS是很重要的队列同步器,内部维护了一个双向链表,来实现线程的等待队列。ReentrantLock、Condition、CountDownLatch、CyclicBarrier、公平锁、非公平锁等很多类方法的实现都是基于这个类。

AbstractQueuedSynchronizer,简称AQS,是一个抽象类,它采用了模板方法模式,降低了自定义同步组件实现的门槛。当需要实现自定义的同步组件时,只需要在自定义组件内部定义一个继承AQS的子类,重写自己需要的方法,再调用AQS提供的模板方法即可。AQS向锁的实现者屏蔽了底层的线程唤醒、阻塞、等待的细节,简化了锁的实现方式。

文中的unpark和唤醒信号是一个意思,表示让线程从阻塞到可以运行态

锁状态 state

private volatile int state
AQS使用一个int成员变量state表示同步状态,当该变量为0时表示没有线程获取到锁。当不为0时说明有线程获取到锁,int>1说明是重入锁,为负数说明重入过多。

Sync Queue中的Node节点

Node是AQS的内部类,
Node中包含共享或者排他的模式(模式本身也是Node节点类型)。
节点的等待状态
SIGNAL=-1

为了避免竞争,加锁方法需要用状态表示是否需要一个信号来唤醒线程,减少竞争。
信号的含义:后续节点已经发出了park信号要求被阻塞

说明后续节点已经或者马上要park发生阻塞,最终一定会park。
所以当 当前节点 释放资源的时候(释放锁或者取消状态即等待超时被中断)如果其等待状态是-1(表示后续节点是阻塞状态),后需要对后续节点,需要发放许可(unpark)来使后续节点唤醒。

CONDITION=-2 表示线程在等待一个条件,说明当前节点正在一个条件等待队列(Condition Queue)中。这个节点被转移出条件等待队列前,即状态改变成0之前,这个节点不会作为(Sync Queue)同步队列中的节点来使用。等待队列中的节点是不能够获取锁的,同步队列中的节点可以获取锁。

PROPAGATE=-3 应该无条件的传播下一个acquireShared,doReleaseShared方法中,头结点被设置成这个状态,保证传播的延续。
CANCELLED=1 因为在同步队列中等待锁超时或者中断,变成取消状态,一个结束状态,该节点在同步队列中被移除,之后状态不会发生改变。
0 不是以上的各种状态。

节点的waitStatus>0 即CANCELLED=1 表示不需要发出信号来unpark当前节点。
waitStatus<=0表示可能需要信号来unpark当前节点
=SIGNAL表示一定会发生阻塞或者已经发生了阻塞。
头节点内部的线程最终会为空,头节点的前驱节点为空

上一个和下一个节点(next prev)
加入这个节点对应的线程 thread
在条件等待队列(Condition Queue)的下个等待节点nextWaiter

Sync Queue

同步队列中的线程有竞争锁的资格。

通过源码发现:同步队列中的线程有竞争锁的资格,对非公平锁上来就竞争锁,对公平锁获取锁的条件是没有节点在队列中等得更久

队列中竞争锁是通过同步队列的头节点的不断出队实现的。头结点需要释放资源,释放资源的方式包括执行完或者等待超时或中断。其他在老二位置的节点(头结点的下一个节点)不断竞争到锁成为头结点。

从等待队列中获得锁的条件是当前节点的前一个节点是头结点,而且头结点释放了资源,tryAcquire成功返回true。
如果有节点在同步队列中竞争到了锁tryAcquire为true,那么会将该节点设置为头结点,并将原来的头结点从同步队列清除。然后通过setHead方法清除后来头结点中的线程。原来头节点中的线程释放锁release(int releases),release释放资源会唤醒后续线程,其他节点才能获得锁进而进行锁竞争。
除了第0个头节点原来线程为空,其他后续加入的头结点线程不为空,头结点肯定已经获得了锁,获得锁之后,就将头结点的线程通过setHead方法赋值为空

private void setHead(Node node) {
    head = node;
    node.thread = null;
    node.prev = null;
}

同步器的队列主要通过下边的两个方法实现:

    /**
     * Inserts node into queue, initializing if necessary. See picture above.
     * @param node the node to insert
     * @return node's predecessor
     */
    private Node enq(final Node node) {// final关键字保证node局部变量不能更改指向
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

    /**
     * Creates and enqueues node for current thread and given mode.
     *
     * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
     * @return the new node
     */
    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;
    }

enq(node) 将当前队列入队列尾部,并返回node的前一个节点
addWaiter(node) 将当前线程的节点加入队列尾部,并返回尾部节点。
在这里插入图片描述

addWaiter是为当前(给定共享或者排他模式模式 加入的node.SHARED或者node.EXCLUSIVE哪个不为空)的线程添加节点,当前同步器的队列尾巴节点(tail node)为空时执行 enq(node) ,不为空时执行代码段node.prev = pred;if (compareAndSetTail(pred, node)) { pred.next = node; return node; }
分析过程分三步:

  • AbstractQueuedSynchronizer初始尾巴节点(tail node)为空,addWaiter方法会执行enq(node)加入第一个节点。方法先初始化生成一个节点(称为第0个节点,第0个节点的线程是Null),然后将开始节点head和尾巴节点tail都设置为这个新生成的第0个节点;然后把第一个节点的前一个节点设置为第0个节点,把尾巴节点tail设置为第一个节点,并把第0个节点的下一个节点设置为第一个节点。
  • AbstractQueuedSynchronizer的tail尾巴节点不为空,addWaiter会执行node.prev = pred;if (compareAndSetTail(pred, node)) { pred.next = node; return node; } 代码段,加入第二个新节点,代码段执行结果是把新加入第二个节点的前一个节点(prev)设置为原来的tail尾巴节点即第一个加入的节点,把第一个节点的下一个节点(next)设置为新加入的第二个节点。并把新加入的第二个节点设置为尾巴节点。
  • 为AQS加入第三个或更多的节点,重复第二步,进行排队,底层用到的是Unsafe类的CAS操作保证线程安全。

独占模式加锁

  /**
     * Acquires in exclusive mode, ignoring interrupts.  Implemented
     * by invoking at least once {@link #tryAcquire},
     * returning on success.  Otherwise the thread is queued, possibly
     * repeatedly blocking and unblocking, invoking {@link
     * #tryAcquire} until success.  This method can be used
     * to implement method {@link Lock#lock}.
     *
     * @param arg the acquire argument.  This value is conveyed to
     *        {@link #tryAcquire} but is otherwise uninterpreted and
     *        can represent anything you like.
     */
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

acquire(int arg) arg可以表示一个锁的状态,0表示线程没有获得锁,1表示获得锁。大于1表示重入锁。用状态来控制锁的原理可以用下边伪代码表示:

class Lock{
	 volatile state=0;// 0说明锁没有被占用
	 
	void lock(int 1){// 锁定方法,获取锁(状态成功改变),获取成功,可以向下执行逻辑
	// 如果期望值是0(未锁定)将其改为1(锁定),成功改为1说明得到了锁,如果没有改成功,继续在循环中CAS尝试
	 	while(!compareAndSet(stateOffset,0,1)){
	 		// 几种让出cpu方式
	 		yield();// 让出cpu执行权
	 		or Thread.sleep(10);// 主动休息一会
	 		or Unsafe.park(false,0L);// 没收许可,禁止线程运行
	 		InWaitqueue(CurrentThread);// 进入等待队列
	 	}
		// 执行逻辑
		logicMethod();
	 }

	void unlock(){// 释放锁
		state=0;
		Unsafe.unPark(the Queue Threads);// 重新给队列中park的线程许可
	}
}

上边这个public final void acquire(int arg)方法是以独占方式获取锁,首先调用tryAcquire(arg),使用了设计模式中的模板设计模式。FairSync和NonfairSync继承了AQS,并重写了tryAcquire(int arg)方法,tryAcquire(int arg)返回true说明成功获取了锁。获取成功后tryAcquire(arg)=true,if条件!tryAcquire(arg)不满足,方法直接结束。如果是公平锁FairSync 那么tryAcquire方法需要通过!hasQueuedPredecessors()条件来保证节点队列中当前线程节点Node之前prev位置不是含有非空线程的Node节点,即保证没有线程比当前线程等的更久 。

AQS中模板方法

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

FairSync中公平实现获得锁

protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {// c等于0说明锁没有被占用
                if (!hasQueuedPredecessors() &&// 查询是否有线程比当前线程等的更久 
                    compareAndSetState(0, acquires)) {// 将锁的状态设置成acquires 1表示锁被占用 CAS
                    setExclusiveOwnerThread(current);// 将当前线程设置为独占线程
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;//已经独占线程,那么就是重入锁。nextc是重入次数
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

NonfairSync非公平实现获得锁

  protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }

Sync中的nonfairTryAcquire(int acquires)方法没有!hasQueuedPredecessors()方法的判断。上来就尝试获取锁,获取不到再讲当前节点加入到同步队列中竞争获取锁。

final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

public final void acquire(int arg)方法执行中,如果tryAcquire(arg)没有获取到锁,返回false,那么会执行从一个不停歇队列中获取锁的方法acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
addWaiter(Node.EXCLUSIVE)。通过查看源码,这个方法首先通过addWaiter(Node.EXCLUSIVE)将一个包含当前线程的Node以以排他形式加入到Node队列中(将当前线程的节点加入队列尾部,并返回尾部节点),然后acquireQueued(final Node node, int arg)从等待队列中获取锁,获取锁方式为不间断一直重试获取锁的方式。执行过程:遍历队列,无限遍历,一直遍历到node的prev节点是 同步队列头head节点,然后执行tryAcquire(如果是公平锁需要保证节点队列中没有其他线程节点比当前线程节点等更久)直到获取到锁为止,获得锁后将node节点排在头结点的位置(所以说头结点从最初的第0个空线程节点,到后来节点获得了锁,将头结点设置为获得了锁的节点,以后往复。因此在头结点中的线程(setHead清空Head节点的线程之前有线程)都已经获得了锁,所以说除了第0个节点,头结点已经获得了锁)。返回true表示当前线程需要中断(shouldParkAfterFailedAcquire),false表示当前线程不需要中断。返回true

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
        // 将一个节点以排他形式加入队列尾部,并返回尾部节点,在队列中尝试以不断判断这个尾部节点的前驱节点是否是头节点(头节点是初始化节点,线程为空),尝试获取锁,获取锁后将当前节点设置为头节点(不需要加锁,或者cas操作head,因为acquireQueued中tryAcquire表示获得了锁)
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();// 改变线程的中断状态
    }

上述public final void acquire(int arg)方法不执行selfInterrupt();返回false什么也不做返回void。

 /**
     * Acquires in exclusive uninterruptible mode for thread already in
     * queue. Used by condition wait methods as well as acquire.
     *
     * @param node the node
     * @param arg the acquire argument
     * @return {@code true} if interrupted while waiting
     */
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
               // 无限遍历,一直遍历到node的prev节点是 同步队列的头head节点
               // 等待head节点释放资源才可以获取锁
                final Node p = node.predecessor();
                // 当前节点的前一个节点是Head节点,并且获得了锁。
                // 从上文enq分析中,第0个节点也即Head节点是一个不含线程的空线程节点
                // 所以公平线程也是可以获得锁即tryAcquire(arg)=true
                if (p == head && tryAcquire(arg)) {
                    // 获得锁将当前线程的节点设置为头节点。
                    // 设置后头节点就含有线程
                    // 除非这个头节点中的线程释放锁release(int arg)
                    // 其他线程才有机会获得锁
                    // tryAcquire(arg)获得了锁,所以可以不加锁
                    setHead(node);// setHead又将Head节点的线程清除,prev节点清除
                    p.next = null; // help GC 将原来的头结点的下个节点设置为空清除头结点
                    failed = false;
                    // 如果不需要且没有终止(shouldParkAfterFailedAcquire(p, node)&&parkAndCheckInterrupt())为false,那么返回false
                    return interrupted;
                }
                // 获取锁失败是否应该Park
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

独占模式释放锁

释放锁public final boolean release(int arg)
如果头结点不为空,且头结点的状态不是0,那么就应该唤醒后续线程。如果是0说明执行过unparkSuccessor方法(会将等待状态变成0),不需要再执行了。

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

看一个释放锁boolean tryRelease(int releases)的实现(在Sync中)

     protected final boolean tryRelease(int releases) {
            int c = getState() - releases;// 状态-1
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);// 设置独占线程为空
            }
            setState(c);
            return free;
        }

唤醒后继节点
如果等待状态小于0用cas变成0。1.如果第一个后继节点是空或者取消状态,那么不需要唤醒,于是从同步队列尾部开始遍历,从尾节点向前找队列中之前节点,一直到找到为空,或者一直找到队列中的当前节点,找到离当前节点后边最近的一个节点(满足waitStatus<=0),其 等待状态小于等于0的后继节点进行唤醒。2.如果第一个后继节点不是空或者取消状态<=0,那么对其进行唤醒。

private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0) // 如果等待状态是小于0 变成0
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {// 后继节点为null,或者后继节点不需要唤醒Cancelled=1
            s = null;
            // 从队列尾部唤醒需要唤醒的线程
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)// 找到小于等于0的状态
                    s = t;
        }
        if (s != null)
        // 使用unpark唤醒后继线程
            LockSupport.unpark(s.thread);
    }

独占模式释放获取锁小结
AQS内部使用一个双向的FIFO队列进行线程状态的管理,当一个线程使用CAS获取同步状态失败!tryAcquire(arg),就会生成一个节点,并将这个节点采用CAS的方式放置到队列末尾addWaiter(Node.EXCLUSIVE)。每一个获取同步状态失败的线程进入FIFO队列成为其中一个节点之后,这个节点都会以死循环的方式获取同步状态 for (;;)。如果获取不到,经shouldParkAfterFailedAcquire(p, node) 判断如果应该中断(park阻塞) node节点,则会阻塞该节点中的线程。

节点进入队列中以死循环的方式获取同步状态的,若此前头节点(即获取到同步状态的节点)释放了同步状态release,该头节点会唤醒它的后继节点unparkSuccessor,这个后继结点被唤醒后,首先检查自己的前驱节点是不是头节点if (p == head

  • 自己的前驱节点头是节点则尝试获取同步状态if (p == head && tryAcquire(arg)),头结点中的线程释放了锁那么后续节点有机会获得锁。
  • 自己的前驱节点不是头结点,获取锁失败,那么通过shouldParkAfterFailedAcquire(p, node) 判断当前线程在获取锁失败后是否应该被park,如果parkAndCheckInterrupt返回true说明park当前线程成功,当前线程的中断状态为true。
shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt()

shouldParkAfterFailedAcquire方法的逻辑:节点是否该阻塞的逻辑

就是node节点的前驱prev节点pred的状态是
Node.SIGNAL=-1就说明node节点已经发出请求(终止park)信号,所以可以安全的park,返回true。
如果pred的ws是cancelled=1 忽略状态,那么忽略所有的cancelled状态的节点,将这些cancelled节点从同步队列中删除并返回false。
如果是0或者3,预示着我们需要(终止park)信号但是还没有park,所以通过 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); 将pred的等待状态变成-1表示发出请求park信号。表示node节点该park了。
调用者需要进一步尝试获取直到node节点发生park。返回false。

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        // 表示node节点该park了
        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.
             */
            // 去掉并忽略cancelled节点
            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.
             */
             // 预示着我们需要(终止park)信号但是还没有park
             // 所以将pred的等待状态变成-1表示发出请求,表示node节点该park了。
             // 如果pred等待状态大于0 就将pred等待状态变成-1表示node节点该park了
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

《Java并发编程的艺术》一书对该过程的总结如下:
当获取同步状态时,同步器维护一个同步队列,获取同步状态失败的线程都会被加入到队列中并在队列中进行自旋;移出队列靠setHead(node);p.next = null; // help GC实现(或停止自旋)的条件是前驱结点为头节点并且该节点成功获取到了同步状态。在释放同步状态时,同步器调用tryRelease(int
arg)方法释放同步状态,并唤醒头节点的后继节点unparkSuccessor(h);

取消获得锁cancelAcquire

cancelAcquire方法先将node节点的线程设置为空,忽略掉取消状态的prev前继节点,如果node节点是尾巴节点,删掉尾巴节点node,将第一个不为取消状态的前pred节点设置为尾巴节点。将predNext设置为空。如果不是尾巴节点或者没有将pred节点设置为尾巴节点,或者如果pred节点线程不是空且不是头节点,且pred节点的等待状态是Signal或者通过cas编程Signal等条件待判断为真,就将pred节点的下一个节点设置为node节点的next节点(compareAndSetNext(pred, predNext, next) cas实现将pred的下一个节点设置为node节点的下一个节点)清除node节点。最后 node.next = node; // help GC将队列中的node节点彻底清除。所以cancelAcquire删除了同步队列中的node节点,将等待状态设置为CANCELLED,如果Node是头结点或者前驱节点的等待状态不是Signal状态唤醒后续线程

 private void cancelAcquire(Node node) {
        // Ignore if node doesn't exist
        if (node == null)
            return;

        node.thread = null;

        // Skip cancelled predecessors
        Node pred = node.prev;
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;

        // predNext is the apparent node to unsplice. CASes below will
        // fail if not, in which case, we lost race vs another cancel
        // or signal, so no further action is necessary.
        // pred节点的下一个节点,下边用cas实现同步队列的重新排序。删除cancelled的节点
        Node predNext = pred.next;

        // Can use unconditional write instead of CAS here.
        // After this atomic step, other Nodes can skip past us.
        // Before, we are free of interference from other threads.
        node.waitStatus = Node.CANCELLED;

        // If we are the tail, remove ourselves.
        if (node == tail && compareAndSetTail(node, pred)) {
            compareAndSetNext(pred, predNext, null);
        } else {
            // If successor needs signal, try to set pred's next-link
            // so it will get one. Otherwise wake it up to propagate.
            int ws;
            // pred节点不是head节点或者是SIGNAL状态
            if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) {
                Node next = node.next;
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {
            // pred是头节点,或者最终等待状态不是-1(是-1需要park后续线程)。
            // 就唤醒node节点的后续节点。
                unparkSuccessor(node);
            }

            node.next = node; // help GC
        }
    }

等待模式 Condition Queue

等待队列中的节点是没有获得锁的资格的,doSignal方法将节点加入到同步队列(Sync Queue)中,才有获得锁的资格。

AQS维护了一个ConditionObject内部类,包括
private transient Node firstWaiter;
private transient Node lastWaiter;
这两个成员变量同AQS中的 Node nextWaiter;共同构成了一个等待队列。

下边是await方法

      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);
        }

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 = new Node(Thread.currentThread(), Node.CONDITION);
    if (t == null)
        firstWaiter = node;
    else
        t.nextWaiter = node;
    lastWaiter = node;
    return node;
}

doSignal方法,将等待队列中的第一个节点移除队列

   private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }

transferForSignal方法,将移除的节点状态设置成0,将移除的节点加入到同步队列,同时如果节点的前一个节点p状态如果大于0(1是取消状态Cancelled)或者compareAndSetWaitStatus(p, ws, Node.SIGNAL)失败,那么unpark当前节点线程。

final boolean transferForSignal(Node node) {
    /*
     * If cannot change waitStatus, the node has been cancelled.
     */
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;

    /*
     * Splice onto queue and try to set waitStatus of predecessor to
     * indicate that thread is (probably) waiting. If cancelled or
     * attempt to set waitStatus fails, wake up to resync (in which
     * case the waitStatus can be transiently and harmlessly wrong).
     */
    Node p = enq(node);// 返回前一个节点
    int ws = p.waitStatus;
    // 前一个节点状态大于0或者没有成为Signal那么就unpark node节点 
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        LockSupport.unpark(node.thread);
    return true;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值