java并发多线程显式锁Condition条件简介分析与监视器 多线程下篇(四)

简介: java并发多线程显式锁Condition条件简介分析与监视器 多线程下篇(四) Lock接口提供了方法Condition newCondition();用于获取对应锁的条件,可以在这个条件对象上调用监视器方法 可以理解为,原本借助于synchronized关键字以及锁对象,配备了一个监视器 而显.

java并发多线程显式锁Condition条件简介分析与监视器 多线程下篇(四)

Lock接口提供了方法Condition newCondition();用于获取对应锁的条件,可以在这个条件对象上调用监视器方法
可以理解为,原本借助于synchronized关键字以及锁对象,配备了一个监视器
而显式锁Lock与Condition则针对于一个锁对象,提供了多个监视器
尽管是提供了多个监视器,但是需要记住,是Lock接口提供方法才能够获取到条件对象,所以这些条件对象仍旧是绑定到某一把锁上的
我相信,只要理解了监视器的概念,对于Condition理解起来是不会存在任何难度的,因为本身就是另外一种实现方式
image_5c81c4bd_31bc
从下图可以直观的感受到Condition是作为Object监视器方法的另外实现
wait在这边叫做await
notify在这边叫做signal
image_5c81c4bd_1aeb

等待

await

在监视器上等待
void await() throws InterruptedException;   ,看得出来,此方法是支持中断的
除非发生以下事件,否则将会持续等待
  • 其他某个线程调用此 Condition 的 signal() 方法,并且碰巧唤醒的是该线程
  • 其他某个线程调用此 Condition 的 signalAll() 方法;
  • 其他某个线程中断当前线程
  • “虚假唤醒” 

awaitUninterruptibly

不支持中断的await方法,void awaitUninterruptibly();
从await()的介绍中看得出来,除非发生提到的四种情况,否则将会是等待状态
而void awaitUninterruptibly();则是await()的不可中断版本,也就是只会有三种情况会跳出等待
  • 其他某个线程调用此 Condition 的 signal() 方法,并且碰巧唤醒的是该线程
  • 其他某个线程调用此 Condition 的 signalAll() 方法;
  • 其他某个线程中断当前线程
  • “虚假唤醒”  

awaitNanos

    long awaitNanos(long nanosTimeout) throws InterruptedException;
支持设置超时的等待,参数为等待的纳秒的long型数值
他在基于await的前提下,新增加了超时跳出,否则将会一直等待,他的跳出条件如下
  • 其他某个线程调用此 Condition 的 signal() 方法,并且碰巧唤醒的是该线程
  • 其他某个线程调用此 Condition 的 signalAll() 方法;
  • 其他某个线程中断当前线程
  • “虚假唤醒”  
  • 已超过指定的等待时间新增
返回值
需要注意的是此方法的返回值,是一个long
我们设置了一个超时时间,那么到底用了多少秒,还剩下多少秒?这个返回值就是这意思:
(nanosTimeout - 实际花费时间)的估算值,如果小于等于0,表示没有剩余时长;如果大于0,可以用来确定如果等待返回了是否还需要继续等待?
比如,你在等朋友A,A说你等我一小时,于是你去睡觉了,结果你睡了半小时就被叫醒了,那么跟你本来要等的时间还差半小时
那还要不要继续等了?还是一定要等到总共一小时呢?
典型用法(来自API):
复制代码
boolean aMethod(long timeout, TimeUnit unit) {
long nanos = unit.toNanos(timeout);
lock.lock();
try {
while (!conditionBeingWaitedFor()) {
if (nanos <= 0L)
return false;
nanos = theCondition.awaitNanos(nanos);
}
// ...
} finally {
lock.unlock();
}
}
复制代码
上面的方法中,如果条件仍旧不满足,但是等待结束了(也就是等待了足够多的时间了),直接返回false;否则将会继续执行,直到等到最后一刻
ps:这种代码风格也就JDK常写,否者这种if形式,估计要被项目经理骂

await(long time, TimeUnit unit)

    boolean await(long time, TimeUnit unit) throws InterruptedException;
此方法也是设置超时时长的等待方法,第一个参数为时长个数,第二个参数为第一个参数的单位
他相当于awaitNanos方法的封装(此处是逻辑上看起来,而不是说就是这个方法的封装)
awaitNanos(unit.toNanos(time)) > 0
所以返回类型为boolean,显然true表示没有等待足够的时间;,false 表示等待了足够时间,也就是等待超时

awaitUntil

    boolean awaitUntil(Date deadline) throws InterruptedException;
awaitUntil类似于await(long time, TimeUnit unit),只不过不是设置超时时长,而是设置截止日期
逻辑上可以把他们理解为一回事,如果没有等待足够时长,那么返回true;如果等待超时那么返回false
常用的逻辑(来自API)
复制代码
boolean aMethod(Date deadline) {
boolean stillWaiting = true;
lock.lock();
try {
while (!conditionBeingWaitedFor()) {
if (!stillWaiting)
return false;
stillWaiting = theCondition.awaitUntil(deadline);
}
// ...
} finally {
lock.unlock();
}
}
复制代码
上面的代码中,如果等待了足够的时长(等待超时),那么就会返回false;如果还有剩余时间,继续等待
 
普通的await()方法和awaitUninterruptibly都是直白的等待,一个支持中断,一个不支持中断
有超时设置的三个方法:
awaitNanos、await(long time, TimeUnit unit)、awaitUntil
都是在await()的基础上对超时时长或者截止日期的设置使用
不过这几个方法会返回剩余的超时时长或者使用boolean指示是否还有剩余
所以如果条件不满足,如果还没等够时间,可以控制继续等待或者退出
而对于Object提供的wait方法,就不能做到这么灵活的控制,要么就条件不满足继续等待,要么醒来后继续做别的事情,没办法相对准确的控制“必须要等待一定的时长”,因为如果wait(一小时),5分钟后,被唤醒了,这个用掉了的五分钟(或者说还剩余55分钟,是不知道的)

唤醒

关于唤醒有与Object提供的类似的两个方法,他们的逻辑含义也是一样的,唤醒一个或者唤醒所有,概念上并没有太多需要注意的事情
void signal();
void signalAll();

总结

Condition本身就是Object中监视器概念的另外实现,所以监视器方法调用,也需要持有锁,也就是说:
调用Condition的await()和signal()等方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用
await系列方法相对于Object提供了更加灵活的使用形式,signal和signalAll的逻辑含义可以认为完全一致
另外需要注意await方法与Object中的wait一样,都会释放当前持有的锁,所以醒来之后,会需要重新获取锁
相关文章
|
12天前
|
监控 算法 Java
jvm-48-java 变更导致压测应用性能下降,如何分析定位原因?
【11月更文挑战第17天】当JVM相关变更导致压测应用性能下降时,可通过检查变更内容(如JVM参数、Java版本、代码变更)、收集性能监控数据(使用JVM监控工具、应用性能监控工具、系统资源监控)、分析垃圾回收情况(GC日志分析、内存泄漏检查)、分析线程和锁(线程状态分析、锁竞争分析)及分析代码执行路径(使用代码性能分析工具、代码审查)等步骤来定位和解决问题。
|
1月前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
58 2
|
1月前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
26 2
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
23 3
|
2月前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
20 2
|
2月前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
34 2
|
21天前
|
数据采集 Java Python
爬取小说资源的Python实践:从单线程到多线程的效率飞跃
本文介绍了一种使用Python从笔趣阁网站爬取小说内容的方法,并通过引入多线程技术大幅提高了下载效率。文章首先概述了环境准备,包括所需安装的库,然后详细描述了爬虫程序的设计与实现过程,包括发送HTTP请求、解析HTML文档、提取章节链接及多线程下载等步骤。最后,强调了性能优化的重要性,并提醒读者遵守相关法律法规。
55 0
|
2月前
|
存储 Java 编译器
[Java]基本数据类型与引用类型赋值的底层分析
本文详细分析了Java中不同类型引用的存储方式,包括int、Integer、int[]、Integer[]等,并探讨了byte与其他类型间的转换及String的相关特性。文章通过多个示例解释了引用和对象的存储位置,以及字符串常量池的使用。此外,还对比了String和StringBuilder的性能差异,帮助读者深入理解Java内存管理机制。
22 0
|
监控 Java 调度
监视锁——Java同步的基本思想
翻译人员: 铁锚翻译时间: 2013年11月13日原文链接: Monitors – The Basic Idea of Java synchronization如果你上过操作系统课程,你就知道监视锁(Monitor)是操作系统同步的一个重要概念,在Java中的同步机制也是基于同样的思想.1. 什么是监视锁?一个监视锁就如同一座大楼里面的一个特殊房间,这个特殊的房间同一时间只可以被一个客户(线程)所使用(就比如单人化妆间,不理解化妆间的可以搜索 更衣),当然,这个"房间"里通常会有一些数据,以及适量的代码。
986 0
|
12天前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####