AQS2--出队

设置前面=-1,就是为了当前面节点变成head执行唤醒时候,能够唤醒后面status正常的节点,

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阻塞了,就要异常的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有可能=head(head=node,node.thread=null),只是还没有执行thread=null,prev肯定也没有执行if(h.waitStatus!=0)的判断。
                //走到第三个判断,前面节点=-1,并且还没有执行出队判断,那么可以建立后驱,即使建立后驱前出队了,也可以唤醒后面 ) { Node next
= node.next; if (next != null && next.waitStatus <= 0) 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();//只有一个线程访问
            }
        }
    }
}

 

转载于:https://www.cnblogs.com/yaowen/p/11323457.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值