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

设置前面节点=-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 != )
unparkSuccessor(h);
Node s = node.next;
if (s == null || s.waitStatus > ) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= )
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可以=-1,head=0出队就A自己去获取锁,head=-1出队唤醒A。所以只要A没有阻塞,就不需要前面节点出队执行if判断之前=-1。


如果A阻塞了,就要异常的B去设置前面=-1,此时,一定要在异常节点B前面节点C出队执行if (h != null && h.waitStatus != 0) 之前把前面C设置-1,如果前面节点出队时候=0,A又阻塞了,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 <= && 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 <= )
//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!=head,C没有异常(此时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 = ;
private Lock lock = null;
Ticket(Lock lock){
this.lock = lock;
}
@Override
public void run() {
lock.lock();
try {
System.out.println("AAAAAAAAAAAA");
} finally {
lock.unlock();//只有一个线程访问
}
}
}
}

AQS2--出队的更多相关文章

  1. C语言实现循环队列的初始化&进队&出队&读取队头元素&判空-2

    /*顺序表实现队列的一系列操作(设置flag标志不损失数组空间)*/ #include<stdio.h> #include<stdlib.h> #define Queue_Si ...

  2. C语言实现链队列的初始化&进队&出队

    /*链表实现队列的一系列操作*/ #include<stdio.h> #include<stdlib.h> #define OK 1 #define ERROR 0 typed ...

  3. 牛客网Wannafly挑战赛15 B车辆安排(模拟)AND C 出队(规律)

    传送门 :B题:点我 C题: 点我 题目描述 有n个队伍,每个队伍的人数小于等于5,每辆车最多坐5个人,要求一个队伍的人都在一辆车上,求最少的车数 输入描述: 第一行n第二行n个数,表示每个队伍的人数 ...

  4. JS优先队列排序。出队时,先找出优先级最高的元素,再按照先进先出出队。

    JS优先队列排序.出队时,先找出优先级最高的元素,再按照先进先出出队. /* * 优先队列 * 出队时,先找出优先级最高的元素,再按照先进先出出队. * */ function Queue(){ th ...

  5. AQS独占式同步队列入队与出队

    入队 Node AQS同步队列和等待队列共用同一种节点结构Node,与同步队列相关的属性如下. prev 前驱结点 next 后继节点 thread 入队的线程 入队节点的状态 INITIAl 0 初 ...

  6. Java定义队结构,实现入队、出队操作

    package com.example.demo; import java.util.ArrayList; public class Queue { ArrayList<Object> l ...

  7. c++使用优先队列时自定义优先出队顺序(和sort)

    优先队列也是一种先进先出的数据结构,元素从队尾入队,从队头出队,但是优先队列相较一般队列多了一个判断优先级的功能,在当前队列中,优先级最高的元素将被第一个删除. 先看一下优先队列的定义 templat ...

  8. Wannafly挑战赛15 C“出队”(约瑟夫环类问题)

    传送门 •参考资料 [1]:浅梦无痕 [2]:Esquecer [3]:My CSDN •题意 n 个人围成一圈,1,2 报数,报 1 的离队,求编号为 x 的第几次出队: •对博文[1]的理解 第一 ...

  9. 3,java数据结构和算法:约瑟夫环出队顺序, 单向环形链表的应用

    什么是约瑟夫环? 就是数小孩游戏: 直接上代码: 要实现这个,只需要理清思路就好了 孩子节点: class Boy{ int no;//当前孩子的编码 Boy next; // 下一节点 public ...

  10. 队列的C++实现(数组)——创建-进队-出队-返回队首元素-清空队列栈-处理队列

    队列的数组实现,从队尾进入,对头删除. 队列长度用标志变量size,它是独立于front和rear的一个变量.size == 0,队列为空.size == capacity,满队列. 一.结点声明 s ...

随机推荐

  1. Java解析复杂JSON数据的一种方法

    1.需解析JSON数据: { "code": 0, "message": "success", "sid": " ...

  2. SpringApplication到底run了什么(下)

    在上篇文章中SpringApplication到底run了什么(上)中,我们分析了下面这个run方法的前半部分,本篇文章继续开工 public ConfigurableApplicationConte ...

  3. HTTP是什么,不是什么?

    HTTP是什么? 不是什么? HTTP: 超文本传输协议,也就是HyperText Transfer Protocol.这是从字面上的理解,如果深一步的理解呢? HTTP又是什么呢? HTTP是什么 ...

  4. 掌握 Async/Await

    摘要: 还不用Async/Await就OUT了.. 原文:掌握 Async/Await 作者:Jartto Fundebug经授权转载,版权归原作者所有. 前端工程师肯定都经历过 JS 回调链狱的痛苦 ...

  5. lower_case_table_names和数据库在Linux和windows平台之间的相互迁移问题

    MySQL关于 lower_case_table_names 的文档 https://dev.mysql.com/doc/refman/5.7/en/identifier-case-sensitivi ...

  6. java接口顺口溜

    原创作品,转载请注明来源,这篇博客我也发到了我的csdnhttps://blog.csdn.net/suyues/article/details/103458086 接口 接口定义全局变量和抽象方法 ...

  7. VUE的element-ui的使用

    我们在自己的网站当中有的时候会用到element-ui的组建 1.如何安装element-ui的组件 在命令行工具当中输入cnpm i element-ui -S, 等待安装 2.如何在vue当中使用 ...

  8. 极简让ingress-nginx最新版(0.25.0)跑起来

    裸步骤: 一,manadatory.yaml apiVersion: v1 kind: Namespace metadata: name: ingress-nginx labels: app.kube ...

  9. django 统计表

    1. 复杂版 统计,通过跨表查询和timedate模块过滤找到 from django.db.models import Count class TongJiView(View): def today ...

  10. load address、entry point、 bootm address以及kernel运行地址

    load address:加载地址   load_addr      mkimage -a entry point: 入口地址     entry_point     mkimage -e bootm ...