技术标签: 笔记 java锁的实现 ConditionObject
一、Condition接口
public interface Condition {
/**
* 暂停此线程直至一下四种情况发生
* 1.此Condition被signal()
* 2.此Condition被signalAll()
* 3.Thread.interrupt()
* 4.伪wakeup
* 以上情况.在能恢复方法执行时,当前线程必须要能获得锁
*/
void await() throws InterruptedException;
//跟上面类似,不过不响应中断
void awaitUninterruptibly();
//带超时时间的await()
long awaitNanos(long nanosTimeout) throws InterruptedException;
//带超时时间的await()
boolean await(long time, TimeUnit unit) throws InterruptedException;
//带deadline的await()
boolean awaitUntil(Date deadline) throws InterruptedException;
//唤醒某个等待在此condition的线程
void signal();
//唤醒所有等待在此condition的所有线程
void signalAll();
}
二、ConditionObject解析
/**
* ConditionObject的实现,其实其维护两个队列
* 1)Condition队列,表示等待的队列,其waitStatus=Node.CONDITION,由firstWaiter和lastWaiter两个属性操控.
* 2)Sync队列,表示可以竞争锁的队列,这个跟AQS一致,waitStatus=0;
* 3)await()把当前线程创建一个Node加入Condition队列,接着就一直循环查其在不在Sync队列
* 如果当前节点在Sync队列里了,就可以竞争锁,恢复运行了.
* 4)signal()方法就是把某个节点的nextWaiter设为null,再把其从Condition队列转到Sync队列
*/
public class ConditionObject implements Condition, java.io.Serializable {
//condition队列的第一个节点
private transient Node firstWaiter;
//condition队列的最后一个节点
private transient Node lastWaiter;
public ConditionObject() { }
/**
* 往condition队列中添加新元素
*/
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();//遍历condition队列,移除其中非CONDITION状态的元素
//lastWaiter非CONDITION状态,上一步遍历清除时lastWaiter发生了变化需要重新赋值
t = lastWaiter;
}
//新建一个CONDITION状态的节点,并将其加在condition队列尾部
Node node = new Node(Node.CONDITION);
if (t == null)//lastWaiter为null
firstWaiter = node;//firstWaiter指向新的node
else
t.nextWaiter = node;//lastWaiter不为null,node拼在lastWaiter后面
lastWaiter = node;//lastWaiter指向新增加在末尾的元素
return node;//返回新增元素
}
/**
* 暂停此线程,直至进入SyncQueue
* 线程唤醒时如果还是不在SyncQueue,会继续park
*/
public final void awaitUninterruptibly() {
Node node = addConditionWaiter();//往condition队列中添加新元素
int savedState = fullyRelease(node);//释放当前线程的锁
boolean interrupted = false;
while (!isOnSyncQueue(node)) {//node节点不在SyncQueue,就一直循环
LockSupport.park(this);//阻塞node的线程
//线程唤醒时,继续循环,如果还是不在SyncQueue,继续park休眠
if (Thread.interrupted())
interrupted = true;
}
//跳出来,说明线程进入SyncQueue了,acquireQueued作用:阻塞node
if (acquireQueued(node, savedState) || interrupted)
selfInterrupt();//中断线程
}
/**
* 释放信号
*/
public final void signal() {
if (!isHeldExclusively())//不是独占模式(排它模式)抛出异常
throw new IllegalMonitorStateException();
//将等待队列的第一个节点出队列,并将其加入AQS的锁队列
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
/**
* Removes and transfers nodes until hit non-cancelled one or
* null. Split out from signal in part to encourage compilers
* to inline the case of no waiters.
* @param first (non-null) the first node on condition queue
*/
private void doSignal(Node first) {
do {
if ((firstWaiter = first.nextWaiter) == null)//第一个元素空了,说明condition队列空了
lastWaiter = null;//最后一个元素引用置空
//将first.nextWaiter置空,也就是从condition队列中出列
first.nextWaiter = null;
//如果转换队列不成功且condition队列不为null,继续循环
} while (!transferForSignal(first) && (first = firstWaiter) != null);
}
/**
* 唤醒所有等待在此condition的所有线程
*/
public final void signalAll() {
if (!isHeldExclusively())//非独占模式,抛出异常
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}
/**
* 唤醒所有等待在此condition的所有线程
*/
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {//循环遍历condition队列,一个个塞到Sync队列中
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}
/**
* 遍历condition队列,移除其中非CONDITION状态的元素
*/
private void unlinkCancelledWaiters() {
Node t = firstWaiter;
Node trail = null;
while (t != null) {//遍历condition队列
Node next = t.nextWaiter;
if (t.waitStatus != Node.CONDITION) {
t.nextWaiter = null;//这样t就从condition中移除了
if (trail == null)
firstWaiter = next;//指向下一个元素,再次遍历
else//trail是上一个元素且为CONDITION状态
trail.nextWaiter = next;//上一个元素与本元素的下一个元素相连,本元素被剔除
if (next == null)
lastWaiter = trail;//没有下一个元素了,lastWaiter指向trail
} else {
trail = t;
}
t = next;
}
}
...
/**
* 参考上面对awaitUninterruptibly(..)的分析
*/
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
/**
* 参考上面对awaitUninterruptibly(..)的分析
*/
public final long awaitNanos(long nanosTimeout) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// We don't check for nanosTimeout <= 0L here, to allow
// awaitNanos(0) as a way to "yield the lock".
final long deadline = System.nanoTime() + nanosTimeout;
long initialNanos = nanosTimeout;
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (nanosTimeout <= 0L) {
transferAfterCancelledWait(node);
break;
}
if (nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
nanosTimeout = deadline - System.nanoTime();
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
long remaining = deadline - System.nanoTime(); // avoid overflow
return (remaining <= initialNanos) ? remaining : Long.MIN_VALUE;
}
/**
* 参考上面对awaitUninterruptibly(..)的分析
*/
public final boolean awaitUntil(Date deadline) throws InterruptedException {
long abstime = deadline.getTime();
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (System.currentTimeMillis() >= abstime) {
timedout = transferAfterCancelledWait(node);
break;
}
LockSupport.parkUntil(this, abstime);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return !timedout;
}
/**
* 参考上面对awaitUninterruptibly(..)的分析
*/
public final boolean await(long time, TimeUnit unit)
throws InterruptedException {
long nanosTimeout = unit.toNanos(time);
if (Thread.interrupted())
throw new InterruptedException();
// We don't check for nanosTimeout <= 0L here, to allow
// await(0, unit) as a way to "yield the lock".
final long deadline = System.nanoTime() + nanosTimeout;
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (nanosTimeout <= 0L) {
timedout = transferAfterCancelledWait(node);
break;
}
if (nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
nanosTimeout = deadline - System.nanoTime();
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return !timedout;
}
...
/**
* 获取condition队列中所有等待的线程并返回
*/
protected final Collection<Thread> getWaitingThreads() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
ArrayList<Thread> list = new ArrayList<>();
for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
if (w.waitStatus == Node.CONDITION) {
Thread t = w.thread;
if (t != null)
list.add(t);
}
}
return list;
}
}
文章浏览阅读889次,点赞11次,收藏16次。本教程从ESP8266-01S固件烧录开始(保姆级教程),详细介绍了如何使用AT指令将ESP8266-01S连接到阿里云物联网平台。对于零基础的用户来说,只要按照教程操作,就能够轻松实现物联网设备的接入和控制。_at+cipsntpcfg
文章浏览阅读338次。第一种,Array.isArray(参数) <script> // Array.isArray(arr) // 函数名- isArray function isArray(arr) { if (Array.isArray(arr)) { console.log('是一个数组'); } else { console.log('不是一个数组'); } } let arr1 = 'a'_封装一个工具函数,传入一个数据,判断是否是数组(异常处理,提升代码健壮性),对传入
文章浏览阅读4.5k次,点赞3次,收藏10次。步骤1:文件位置:Saber/src/mock/menu.js新增菜单模块:步骤2:绑定对应模块路由:(与步骤1中的模块菜单名称对应)在zh.js中添加新增菜单的名称:(与对应路由名称绑定)新建一个页面,路径与步骤1中页面路径对应 (Saber/src/views/util/test.vue),编写页面:完成效果图如下:附上test.vue的完整测试代码:<t..._saber 新增菜单
文章浏览阅读3.3k次,点赞2次,收藏2次。安装完成后,这些包将会被安装在您的 CentOS 系统上。打开终端,使用 root 或具有管理员权限的用户登录。_yum install libssl-dev
文章浏览阅读747次。以前粗略的学习过Matplotlib绘图、Pandas绘图(这里是pandas的常见绘图总结),但是都未深入的去学习过,一遇到问题就翻..._seaborn清晰度
文章浏览阅读1.1k次。FX5U FX3U MX 三菱 PLC _mx component通讯fx3u 64位
文章浏览阅读400次。1、首先要在Gradle 里面设置dependencies2、在UI XML里面添加RecyclerView3、需要编写一个RecyclerView.Adapter 3.1 Adapter里面要编写RecyclerView.Viewholder: 这是RV里面显示的小单元_androidx.recyclerview:recyclerview:1.1.0
文章浏览阅读3.3k次。1、最近想用下CKEditor5 新特性,但发现上传不上去,经调研官网调研如下:1、首先引入jshttps://cdn.ckeditor.com/ckeditor5/17.0.0/classic/ckeditor.js2、html相关代码<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><..._vue+ckeditor上传图片不显示
文章浏览阅读1.9k次,点赞2次,收藏24次。1、在Qt designer中设计界面,包含一个QGroupBox和QWidget:2、导入需要的库:import matplotlibmatplotlib.use("Qt5Agg") # 声明使用QT5from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvasimport matplotlib.pyplot as plt3、编写绘图函数:def plot_figure(self, ax_alphalens 图表显示在pyqt中
文章浏览阅读97次。docker 搭建自己的gitlabhttps://www.cnblogs.com/smallstudent/p/5396660.html--privileged=true 容器拥有root权限172.17.0.8--hostname 111.229.235.9 服务器ip地址docker pull gitlab/gitlab-ce:8.7.0-rc3.ce.0mkdir -p /opt/gitlab/config/opt/gitlab/logs/opt..._gitlab 搭建 docker
文章浏览阅读5.7k次,点赞4次,收藏62次。达梦数据库使用安装数据库一、安装规划1、创建用户组、用户、安装目录[root@Kylin10 ~]# groupadd dinstall[root@Kylin10 ~]# useradd -g dinstall -m -d /home/dmdba -s /bin/bash dmdba[root@Kylin10 ~]# passwd dmdba 更改用户 dmdba 的密码 。新的 密码:重新输入新的 密码:passwd:所有的身份验证令牌已经成功更新。[root@Kylin10 ~]#_mac安装达梦数据库
文章浏览阅读61次。问题的提出相传古时候有个退休的程序员, 在家闲来无事,决定修习书法之道。第一日, 备好笔墨纸砚,便挥毫写下一行大字: “Hello World ”。学过编程语言的人都笑了,在程序员心目中, hello world 是一切的开始,程序语言教科书的第一个演示程序、 ...