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 ...
随机推荐
- open live writer安装以及代码高亮、折叠插件安装
一.目的 方便在本地写博客,不用在浏览器上写. 二.open live writer的安装 下载open live writer 这是我的 链接:https://pan.baidu.com/s/1u8 ...
- 并发编程-线程,JMM,JVM,volatile
1.线程 相信大家对线程这个名词已经很不陌生了,从刚开始学习java就接触到线程,先说说进程吧,进程就是系统分配资源的基本单位,线程是调度cpu的基本单位,进程由线程组成,一个进程至少又一个线程组成, ...
- Unity Ioc 依赖倒置及Untity AOP被动拦截/自动拦截
各位博友金安,首先声明这是一篇转载的博客,原文链接:https://www.cnblogs.com/scottpei/archive/2013/01/08/2851087.html 十年河东,十年河西 ...
- 微信小程序环境配置和开发!!
1.登陆微信公众平台小程序,下载 普通小程序开发者工具.或者 小游戏开发者工具. 2.新建项目需要填以下几点,然后初始demo如下,注意rpx是分成750份的单位. 3.点击预览,用微信扫描二维码,代 ...
- EHLIB 安装方法
Ehlib安装方法 路人甲 2010-05-05 23:01:37 安装文件自带的Readme.txt中的安装过程如下: 1. Delphi 5.x - 7.x, Delphi 9.X Win32, ...
- elasticsearch 安装 可视化工具
一.windows下安装Elasticsearch首先计算机需要JAVA环境(已有次此环境跳过)1.java环境安装网址:http://www.oracle.com/technetwork/java/ ...
- springboot 启动停止脚本
https://www.cnblogs.com/lovychen/p/6211209.html 参考 centos 转码解决方案: yum install dos2unix dos2unix ** ...
- MySQL连接超时处理
1.由于MySQL默认是8小时的wait_timeout,当超过8小时的连接时间后,在JAVA中调用将出现如下报错 SEVERE EXCEPTION com.mysql.jdbc.exceptions ...
- Jmeter(四十四)启动提示 Could not open/create prefs root node Software\JavaSoft\Prefs at root 0x80000002. Windows RegCreateKeyEx(...) returned error code 5.
有已知的已知:有些事情我们自己知道自己知道: 我们也知道有已知的未知:这是指我们知道有些事情自己不知道: 但是还有未知的未知:有些事情我们不知道自己不知道: ---美国国防部长 唐纳德·拉姆斯菲尔 ...
- 用session实现的用户登陆,客户端是怎样获取到cookie信息的
大家都知道cookie是存在客户端,session存在服务器端.那么客户端具体是怎样获取cookie信息的呢? 更好的阅读体验可访问 这里. 实验环境 实验环境:xampp + Thinkphp5 + ...