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 != )
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--出队的更多相关文章
- C语言实现循环队列的初始化&进队&出队&读取队头元素&判空-2
/*顺序表实现队列的一系列操作(设置flag标志不损失数组空间)*/ #include<stdio.h> #include<stdlib.h> #define Queue_Si ...
- C语言实现链队列的初始化&进队&出队
/*链表实现队列的一系列操作*/ #include<stdio.h> #include<stdlib.h> #define OK 1 #define ERROR 0 typed ...
- 牛客网Wannafly挑战赛15 B车辆安排(模拟)AND C 出队(规律)
传送门 :B题:点我 C题: 点我 题目描述 有n个队伍,每个队伍的人数小于等于5,每辆车最多坐5个人,要求一个队伍的人都在一辆车上,求最少的车数 输入描述: 第一行n第二行n个数,表示每个队伍的人数 ...
- JS优先队列排序。出队时,先找出优先级最高的元素,再按照先进先出出队。
JS优先队列排序.出队时,先找出优先级最高的元素,再按照先进先出出队. /* * 优先队列 * 出队时,先找出优先级最高的元素,再按照先进先出出队. * */ function Queue(){ th ...
- AQS独占式同步队列入队与出队
入队 Node AQS同步队列和等待队列共用同一种节点结构Node,与同步队列相关的属性如下. prev 前驱结点 next 后继节点 thread 入队的线程 入队节点的状态 INITIAl 0 初 ...
- Java定义队结构,实现入队、出队操作
package com.example.demo; import java.util.ArrayList; public class Queue { ArrayList<Object> l ...
- c++使用优先队列时自定义优先出队顺序(和sort)
优先队列也是一种先进先出的数据结构,元素从队尾入队,从队头出队,但是优先队列相较一般队列多了一个判断优先级的功能,在当前队列中,优先级最高的元素将被第一个删除. 先看一下优先队列的定义 templat ...
- Wannafly挑战赛15 C“出队”(约瑟夫环类问题)
传送门 •参考资料 [1]:浅梦无痕 [2]:Esquecer [3]:My CSDN •题意 n 个人围成一圈,1,2 报数,报 1 的离队,求编号为 x 的第几次出队: •对博文[1]的理解 第一 ...
- 3,java数据结构和算法:约瑟夫环出队顺序, 单向环形链表的应用
什么是约瑟夫环? 就是数小孩游戏: 直接上代码: 要实现这个,只需要理清思路就好了 孩子节点: class Boy{ int no;//当前孩子的编码 Boy next; // 下一节点 public ...
- 队列的C++实现(数组)——创建-进队-出队-返回队首元素-清空队列栈-处理队列
队列的数组实现,从队尾进入,对头删除. 队列长度用标志变量size,它是独立于front和rear的一个变量.size == 0,队列为空.size == capacity,满队列. 一.结点声明 s ...
随机推荐
- saltstack手册(含官方pdf)
官方手册 https://docs.saltstack.com/en/pdf/Salt-2019.2.1.pdf 快速入门 SALTSTACK是什么? Salt是一种和以往不同的基础设施管理方法,它是 ...
- Linq 用得太随意导致的性能问题一则
问题场景 有一个很多条数据的数据库(数据源),在其中找出指定的项,这些项的 ID 位于 给定的列表中,如 TargetList 中. private readonly IDictionary<s ...
- Python - 记录我开始学习Python的时间节点
记录我开始学习Python的时间节点 2019-09-22 从明天开始我要开始学习Python了,坚持学习.坚持写博客,慢慢积累. 结合实例项目,最好能把目前在做的项目用Python实现. 加油!
- Spark GraphX图计算核心源码分析【图构建器、顶点、边】
一.图构建器 GraphX提供了几种从RDD或磁盘上的顶点和边的集合构建图形的方法.默认情况下,没有图构建器会重新划分图的边:相反,边保留在默认分区中.Graph.groupEdges要求对图进行重新 ...
- CentOS6安装部署配置Nginx
操作系统: CentOS 6.9 脚本语言: shell https://github.com/iscongyang/Practical/blob/master/shell-scripts/insta ...
- Linux 批量查找并替换文件夹下所有文件的内容
1.批量查找某个目下文件的包含的内容 cd etc grep -rn "查找的内容" ./ 2.批量替换某个目下所有包含的文件的内容 cd etc sed -i "s/查 ...
- texlive相关问题
1.The font "WenQuanYi Micro Hei" cannot be found. 解决方法:下载WenQuanYi Micro Hei的字体(以ttf结尾),粘贴 ...
- Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.qingmu.mybaitsplus.mapper.UserMapper' available:
java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.conte ...
- HTML元素脱离文档流的三种方法
一.什么是文档流? 将窗体自上而下分成一行一行,并在每行中按从左至右依次排放元素,称为文档流,也称为普通流. 这个应该不难理解,HTML中全部元素都是盒模型,盒模型占用一定的空间,依次排放在HTML中 ...
- el获取对象属性大小写问题
当el表达式首字母大写,会判断第二个字母大小写,小写,抛异常:大写,则直接拼接get,形成getter方法 当el表达式首字母小写,会判断第二个字母大小写,大写,必须第一个字母大写:否则无法获取值 e ...