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 ...
随机推荐
- 在 VSCode 调试过程中,使用 Watcher,免手动重新编译
1.安装Microsoft.DotNet.Watcher.Tools包 dotnet add package Microsoft.DotNet.Watcher.Tools --version 2.0. ...
- Python中的函数(高级使用)
一.将函数作为变量去使用 在Python中函数也是一种对象,是一种名为function的对象,所以可以将函数赋值给变量,类似整数.浮点数.列表.元组.字典赋值给变量. 我们编写如下程序: #codin ...
- python--进程初识详解
进程:通俗理解一个运行的程序或者软件,进程是操作系统资源分配的基本单位 1.1.导入进程模块 import multiprocessing 1.2.Process进程类的语法结构如下: Process ...
- java--static与代码块
static与代码块: static class Student{ static String school; // 随着类的加载而执行 可以由类进行调用 static { // 静态代码块 加载类时 ...
- XSS靶场练习
0x00:前言 一个XSS练习平台,闯关形式,一共20关 0x01:开始 第一行都是代码插入点,下面几行是payloads(插入点和payloads中间空一行) LV1 <script>a ...
- cpio建立、还原备份档
1. 简介 加入.解开cpio或tar备份档内的文件 与tar相似,将文件归档到硬盘或磁带等存储设备中 2. tar比较 在所处理的文件类型方面,它比tar更全面,但也更复杂 cpio比tar更为可靠 ...
- Android源码分析(八)-----系统启动流程&IPC简述
一 :系统启动流程图 从下往上依次启动linux kernel -->zygote-->SystemServer-->NativeService-->AndroidServic ...
- oracle linux 7 yum报错解决:COULD NOT RESOLVE HOST: YUM.ORACLE.COM
虚拟机中yum报错 [root@localhost ~]# yum -y install oracle-rdbms-server-11gR2-preinstall Loaded plugins: la ...
- 线上IIS应用程序池自动关闭
事情的经过是这样的: 下午下班的铃声已经敲响,我已经整装待发.突然同事说某水司的微信公众号不能正常访问了.点击营业厅,直接提示Service Unavailable. 立马远程服务器查看,IIS微信公 ...
- Python在Linux环境中安装Thrift
1.文件下载:thrift-0.11.0.tar.gz 个人网盘下载:链接:https://pan.baidu.com/s/1MXgx8LuN4wk7ssVUD9Wzaw 提取码:xw85 2. ...