Talk about the understanding of java threads (5) -------- ReentrantLock's blocking queue

笔下天地宽


接着上一章继续说,上次我们讲到了一个关键字段waitStatus,这个字段对队列的管理很重要。我们下面来详细说一下。上一章也说了,
每个队列的节点都有自己的状态,方便更好的利用资源和管理队列,Node节点其实是对等待线程的一个装,其中包含了线程的信息以及等待状态 ,如是否被阻塞、是否等待、是否需要删除等等,这个状态的控制就是交给waitStatus的,下面先说下waitStatus。

waitStatuswaitStatus共有5个值,CANCELLED、SIGNAL、CONDITION、PROPAGATE、0。看下AQS里面的定义。


 

/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;


下面说明下:​​​
 CANCELLED(1):表示当前节点已经取消调度,当超时或者中断会触发变更为此状态,进入该状态后节点将不会再变化。
 SIGNAL(-1) :表示后继节点在等待当前节点唤醒。后继节点入队时,会将前继节点状态更新为SINGAL。
 CONDITION(-2): 表示节点等待在Condition上(条件队列,后面再说),当其他线程调用了Condition的signal()方法后,CONDITION状态的节点将从等待移到阻塞队列中,等待获取资源。
 PROPAGATE(-3): 表示共享模式下(实现共享锁),前继节点不仅会唤醒它的后继节点,同时也可能唤醒后继节点的后继节点。
 0:新节点入队时的默认状态。

可以看到节点处于有效的等待状态的话都是负值,而正值对应的是节点被取消,所以很多地方用大于0或者小于0来判断节点状态是否正常。下面从百度找了一张图,大家可以先看下。

Blocking queue * diagram

下面继续说代码,上一章讲到了shouldParkAfterFailAcquire这个方法,下面来看下。

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            //直接返回
            return true;
        if (ws > 0) {
            // 大于0的状态只有1,表示这个线程的调度(抢占资源操作)取消了,那就把这个线程移除掉,放着也没用了
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            // 否则就把当前线程的状态修改为SIGNAL
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

Let’s see below,

private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this); //当前线程等待
        return Thread.interrupted(); 返回当前现在interrupt
    }

这个不多说了,看下面一个方法cancelAcquire

private void cancelAcquire(Node node) { //出现异常,取消获取锁操作
        // 如果当前节点不存在,当然也就不用管了
        if (node == null)
            return;

        node.thread = null;

        // 跳过取消的前置节点
        Node pred = node.prev;
        while (pred.waitStatus > 0) //大于0 ,代表是取消
            node.prev = pred = pred.prev;
            Node predNext = pred.next; 
            node.waitStatus = Node.CANCELLED;

        // 如果是尾节点,直接删除
        if (node == tail && compareAndSetTail(node, pred)) {
            compareAndSetNext(pred, predNext, null);
        } else {
            int ws; 
             // 节点状态判断重新设置,只要是节点的操作,就不多说了
            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 {
                unparkSuccessor(node); //解除等待
            }

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

Let's stop here first, let's talk about releasing the lock below! Thanks for watching.

No sacifice,no victory!!

Guess you like

Origin blog.csdn.net/zsah2011/article/details/108801227