AQS2--出队

 队列不卡死,一定要:前面节点变成头结点唤醒时候能够唤醒后面节点,依次类推。

设置前面节点=-1就是为了前面节点走的时候,唤醒自己。

  

正常没有阻塞节点,设置前面=-1,再旋转一次尝试获取锁,才阻塞。即使设置前面节点=-1之前,前面节点=0唤醒失败走了,也不要紧,自己会再次旋转一次获取锁。设置-1之后,前面节点=-1唤醒成功。自己旋转获取锁失败阻塞,前面节点=0唤醒失败走了,是不可能的,因为阻塞前会设置前面=-1.

正常节点阻塞了,前面正常节点=0,那么中间至少有一个异常节点,并且阻塞在异常节点上了。异常节点会去设置前面节点=-1。异常节点赶在前面节点唤醒之前设置-1就可以,赶在之后,那么异常节点就要自己唤醒后面节点。

  

可以把异常节点作为前驱(错了),就是不能把已经执行了if判断的头节点作为前驱,不然就卡死了。

public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0) 
                unparkSuccessor(h);
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);

一定要注意,前面节点变成head执行if (h != null && h.waitStatus != 0) 之前要设置为-1否则就不能唤醒后面status正常节点(特例:除非后面节点自行出队,还在旋转)


 所以正常节点要阻塞之前设置-1.

上图中,A没有阻塞,head出队执行if (h != null && h.waitStatus != 0)时候可以=0可以=-1head=0出队就A自己去获取锁,head=-1出队唤醒A。所以只要A没有阻塞,就不需要前面节点出队执行if判断之前=-1。


 如果A阻塞了,就要异常的B去设置前面=-1,此时,一定要在异常节点B前面节点C出队执行if (h != null && h.waitStatus != 0) 之前把前面C设置-1如果前面节点出队时候=0A又阻塞了,A就永远获取不了锁,队列卡死。

但是,问题在于,正常节点和异常节点都无法知道前面节点是否已经出队执行过了if (h != null && h.waitStatus != 0)。但是前面节点变成head时候thread=null,所以后面节点能够知道的前面节点是否是head。所以一定要在异常节点B前面节点C变成head之前把前面C设置-1(或者已经变成了head但是还没有执行if(h.wautStatus!-0)的判断),否则就唤醒A

 下面看异常节点设置前面节点=-1逻辑:

if (pred != head
&&( (ws = pred.waitStatus) == Node.SIGNAL|| ( ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) //pred有可能=head,
    && pred.thread != null //pred.thread!=null,那么pred肯定不=1,因为等于1之前thread就设为null了,那么此时prev肯定=-1(-1不可能变成0)。
或者pred异常了,但是thread!=null status=-1,作为前驱也可以,相当于先认定一个前驱,再这个前驱异常,只要不把执行了if判断的头结点作为前驱就可以。
                //pred有可能=head(head=node,node.thread=null),只是还没有执行thread=null,prev肯定也没有执行if(h.waitStatus!=0)的判断。
                //走到第三个判断,前面节点=-1,并且还没有执行出队判断,那么可以建立后驱,即使建立后驱前出队了,也可以唤醒后面 ) { Node next
= node.next; if (next != null && next.waitStatus <= 0)
//pred如果正常,肯定=-1,刚刚pred.thread!=null,现在可能thread=null了,也就是可能作为head进行if判断过了,但是设置-1之前如果是头结点肯定没有执行if判断,可以唤醒。
//pred如果是异常,建立后驱关系也不要紧,这个异常的pred会再去调整。这里也是尽可能使得队列不卡死. compareAndSetNext(pred, predNext, next); }
else { unparkSuccessor(node); }
head = node;
node.thread = null;
node.prev = null;

1.如果异常节点前面节点=head

 此时不知道head有没有执行if (h != null && h.waitStatus != 0),如果没有执行,此时设置head=-1,那么head执行if (h != null && h.waitStatus != 0)就回去唤醒A,如果已经执行了,就不会唤醒任何节点,再去将head=-1,也没用,所以就去唤醒异常节点的后面节点。

 

2.如果C不是头节点,能够设置C=-1,并且设置C=-1之后C还不是头节点(C.thread != null)(或者是头节点但是没有执行if(h!=null&&h.waitStatus!=0))(异常节点也可以作为前驱),就建立后驱关系(即使还没有建好后驱关系,C就开始出队,但是C=-1了,C一定能够唤醒A)。这就保证了在异常节点B前面节点C变成head之前把前面C设置-1(即使C变成head但是没有执行if(h.waitStatus!=0)判断。其余情况都不行。

 如果异常节点前面C!=head,但是C=1了(此时C可以=head),肯定不能建立后驱关系,C节点就一定可以保证A能够唤醒,如果B再向前找没必要。B来唤醒,我个人感觉是没必要的,因为C可以一定唤醒A

 如果异常节点前面C!=headC没有异常(此时C可以=head),但是C是头节点了(C.thread = null),此时唤醒A

  

注意:C变成head,再去unlock执行if (h != null && h.waitStatus != 0)中间有很多时间。所以C变成了head不一定执行了if(h.waitStatus!=0)的判断。

 

public class testLock {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock1();
        Ticket ticket = new Ticket(lock);
        Thread t0 = new Thread(ticket, "0号窗口");
        t0.start();
        Thread t1 = new Thread(ticket, "1号窗口");
        t1.start();
        Thread t2 = new Thread(ticket, "2号窗口");
        t2.start();
        Thread t3 = new Thread(ticket, "3号窗口");
        t3.start();
        Thread t4 = new Thread(ticket, "4号窗口");
        t4.start();
        Thread t5 = new Thread(ticket, "5号窗口");
        t5.start();
        Thread t6 = new Thread(ticket, "6号窗口");
        t6.start();
        
        t1.interrupt();
    }

    static class Ticket implements Runnable {
        private int num = 3;
        private Lock lock = null;
        Ticket(Lock lock){
            this.lock = lock;
        }
        @Override
        public void  run() {
            lock.lock();
            try {
                System.out.println("AAAAAAAAAAAA");
            } finally {
                lock.unlock();//只有一个线程访问
            }
        }
    }
}

 

posted @ 2019-08-08 20:00  无天666  阅读(393)  评论(0编辑  收藏  举报