当前位置: 首页 > news >正文

用通知-等待机制优化锁等待问题

破坏占用且等待就可以避免死锁产生,以上一节中的循环等待代码来看:

// 一次性申请转出账户和转入账户,直到成功
while(!actr.apply(this, target))
 ;

如果apply()操作耗时非常端,而且并发冲突量不大时,这个方案是不错的,因为这种场景下,循环上几次或者几十次就可以一次性获取锁,执行业务。但是如果apply()操作耗时长,或者并发冲突量很大的时候,循环等待就可能循环上万次才能获取锁,会导致CPU使用率升高,影响环境的问题。

这种场景,更好的解决方案是:如果线程要求的条件不满足,则线程阻塞自己,进入等待状态,在满足线程要求的条件后,通知等待的线程重新执行。

完整的等待-通知机制:线程首先获取互斥锁,当线程要求的条件不满足时,释放互斥锁,进入等待状态;当要求的条件满足时,通知等待的线程,重新获取互斥锁。

用synchronized实现等待-通知机制

在Java语言中,使用synchronized配合wait()、notify()、notifyAll()这三个方法就可以轻松实现。

image

如图,左边一个等待队列,同一时刻,只允许一个线程进入synchronized保护的临界区,当有一个线程进入临界区后,其他线程只能进入图中左边的队列等待,这个等待队列和互斥锁是一对一的关系,每个互斥锁都有自己独立的等待队列。

进入临界区后,由于不满足部分条件,需要进入等待状态,Java对象的wait()方法就能满足这个需求,当调用wait()方法后,当前线程就会被阻塞,并且进入右侧的等待队列,这个等待队列也是互斥锁的等待队列。线程在进入右侧等待队列的同时,会释放持有的互斥锁,线程释放后,其他线程就有机会获得锁,进入临界区。

当线程条件满足时,通知等待的线程,可以使用Java对象的notify()和notifyAll()方法,通知等待队列中的线程,条件曾经满足过,可以再次尝试获取锁,并判断条件是否满足。

对之前的代码进行优化

 while(条件不满足) {
   wait();
 }
class Allocator {
 private List<Object> als;
 // 一次性申请所有资源
 synchronized void apply(
   Object from, Object to){
   // 经典写法
   while(als.contains(from) ||
        als.contains(to)){
     try{
       wait();
     }catch(Exception e){
     }   
   } 
   als.add(from);
   als.add(to);  
 }
 // 归还资源
 synchronized void free(
   Object from, Object to){
   als.remove(from);
   als.remove(to);
   notifyAll();
 }
}

用notifyAll()而没有使用notify(),是因为notify()会随机通知等待队列中的一个线程,而notifyAll()会通知等待队列中所有的线程,第一反应是同一时间只有一个线程进入临界区,用notify()就可以了,实际上notify()可能导致某些线程永远不会被通知到。

学习来源:极客时间 《Java 并发编程实战》学习笔记 Day04

相关文章:

  • 微电网(风、光、储能、需求响应)【Simulink 仿真实现】
  • Mysql安全之权限用户管理参考手册
  • C语言萌新如何使用printf函数?
  • 【Kotlin】类的继承 ① ( 使用 open 关键字开启类的继承 | 使用 open 关键字开启方法重写 )
  • [网鼎杯 2020 青龙组]AreUSerialz
  • Windows 服务器刷题(带答案)
  • docker入门(二):docker的常用命令
  • colab 如何释放gpu显存?
  • CANoe-仿真总线上的红蓝线、“CANoe DEMO“ license下的软件限制
  • SpringBoot 参数接收只看这一篇文章就够了
  • Vector - VT System - 模拟IO板卡_VT2816
  • 云原生|kubernetes|2022年底cks真题解析(1-10)
  • 7个实用的DTC品牌出海营销策略,打造强大的品牌竞争力
  • 浅谈Android下的注解
  • 【初阶数据结构】——写了将近 5 万字,终于把 二叉树 初阶的内容讲清楚了
  • c++通讯录管理系统
  • jvm系列(1)--JVM和Java体系架构
  • 线程池ThreadPoolExecutor源码解析
  • 如何减少频繁创建数据库连接的性能损耗?
  • WebSocket长连接接入支付宝消息服务,实现消息通知
  • 中公教育:去年全面扭亏,经营性现金流增长169.6%
  • 云南昭通黄吉庆寻子29年终团聚:儿子在同事鼓励下回乡认亲
  • 特朗普支持率降至新低:宣布关税后骤降,选民最不满经济表现
  • 继续免费通行!五一假期全国高速公路日均流量约6200万辆
  • 医改经验如何因地制宜再创新?国家卫健委“以例说法”
  • 网上销售假冒片仔癀和安宫牛黄丸,两人被判刑