详解Java程序并发的Wait-Notify机制

软件编程 java 分类:[default] 更新日期: 2015-09-28
这篇文章主要介绍了详解Java程序并发的Wait-Notify机制,多线程并发是Java编程中的重要部分,需要的朋友可以参考下

Wait-Notify场景
典型的Wait-Notify场景一般与以下两个内容相关:
1. 状态变量(State Variable)
当线程需要wait的时候,总是因为一些条件得不到满足导致的。例如往队列里填充数据,当队列元素已经满时,线程就需要wait停止运行。当队列元素有空缺时,再继续自己的执行。
2. 条件断言(Condition Predicate)
当线程确定是否进入wait或者是从notify醒来的时候是否继续往下执行,大部分都要测试状态条件是否满足。例如,往队列里添加元素,队列已满,于是阻塞当前线程,当有其他线程从队列里取走了元素,就通知在等待的线程“队列有剩余空间,可以往里添加元素了”。这时,等待添加元素的进程就会被唤醒,然后判断一下当前队列是否真的有剩余空间,如果真的有剩余空间,就将元素添加进去,如果没有,则继续阻塞等待下次唤醒。
3. 条件队列(Condition Queue)
每个对象都有一个内置的条件队列,当一个线程在该对象锁上调用wait函数的时候,就会将该线程加入到该对象的条件队列中。

注意
wait与notify是Java同步机制中的重要组成部分。结合与synchronized关键字使用,可以建立很多优秀的同步模型,例如生产者-消费者模型。但是在使用wait()、notify()、notifyAll()函数的时候,需要特别注意以下几点:

    wait()、notify()、notifyAll()方法不属于Thread类,而是属于Object基础类,也就是说每个对象都有wait()、notify()、notifyAll()的功能。因为每个对象都有锁,锁是每个对象的基础,因此操作锁的方法也是最基础的。
    调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj){...} 代码段内。
    调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj){...} 代码段内唤醒线程A。
    当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。
    如果线程A1,A2,A3都在obj.wait(),则线程B调用obj.notify()只能唤醒线程A1,A2,A3中的一个(具体哪一个由JVM决定)。
    如果线程B调用obj.notifyAll()则能全部唤醒等待的线程A1,A2,A3,但是等待的线程要继续执行obj.wait()的下一条语句,必须获得obj锁。因此,线程A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。
    当线程B调用obj.notify()或者obj.notifyAll()的时候,线程B正持有obj锁,因此,线程A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到线程B退出synchronized代码块,释放obj锁后,线程A1,A2,A3中的一个才有机会获得对象锁并得以继续执行。


示例代码
线程的wait操作的典型代码结构如下:

  public void test() throws InterruptedException { 
    synchronized(obj) { 
      while (! contidition) { 
        obj.wait(); 
      } 
    } 
  } 

为什么obj.wait()操作必须位于循环中呢?有以下几个主要原因:
1. 一个对象锁可能用于保护多个状态变量,当它们都需要wait-notify操作时,如果不将wait放到while循环中就会有问题。例如,某对象锁obj保护两种状态变量a和b,当a的条件断言不成立时发生了wait操作,当b的条件断言不成立时也发生了wait操作,两个线程被加入到obj对应的条件队列中。现在若改变状态变量a的某操作发生,在obj上调用了notifyAll操作,则obj对应的条件队列里的所有线程均被唤醒,之前等待a的一个或几个线程去判断a的条件断言可能成立了,但是b对于的条件断言肯定仍不成立,而此时等待b的线程也被唤醒了,所以需要循环判断b的条件断言是否满足,如果不满足,则继续wait。
2. 多个线程wait的同一状态的条件断言。例如,向队列添加元素的场景,当前队列是满的,多个线程想往里面添加元素,于是都wait了。此时,另一个线程从队列里取出了一个元素,调用了notifyAll操作,唤醒了所有线程,但是只有一个线程能够往队列里添加一个元素,其他的仍需要等待。
3. 虚假唤醒。在没有被通知、中断或超时的情况下,线程自动苏醒了。虽然这种情况在实践中很少发生,但是通过循环等待可以杜绝这一情况的发生。


> 本站内容系网友提交或本网编辑转载,其目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及作品内容、版权和其它问题,请及时与本网联系,我们将在第一时间删除内容!

相关文章
  • Yii2rbac权限控制之rule教程详解
    Yii2rbac权限控制之rule教程详解
    这篇文章主要介绍了Yii2 rbac权限控制之rule教程详解的相关资料,非常不错具有参考借鉴价值,需要的朋友可以参考下在我们之前Yii2搭建后台并实现rbac权限控制完整实例教程中,不知道你曾经疑惑过没有一个问题,rule表是做什么的,为什么在整个过程中我们都没有涉及到这张表? 相信我不说,部分人也都会去尝试,或百度或google,到头来也会竹篮打水,这部 ...
  • 详解表单验证正则表达式实例(推荐)
    这篇文章主要介绍了详解表单验证正则表达式实例推荐的相关资料,非常不错,具有参考借鉴价值,特此分享到平台供大家参考验证:!reg.test(value) 邮箱: 代码如下:reg = /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/i; 不包含中文: 代码如下:r ...
  • .NET实现简易的文件增量备份程序
    这篇文章主要介绍了.NET实现简易的文件增量备份程序,需要的朋友可以参考下.Net中提供了许多方便使用的方法,包括在处理文件中查找文件.拷贝文件等,今天实现的是通过简易的程序实现增量的备份文件. 首先需要的是选择备份源文件路径SourcePath和备份目标文件路径DestinationPath,然后通过StopWatch统计拷贝所耗费的时间.(注意:使用St ...
  • php项目开发中用到的快速排序算法分析
    这篇文章主要介绍了php项目开发中用到的快速排序算法,结合实例形式详细分析了php快速排序的原理与使用方法,需要的朋友可以参考下本文实例讲述了php项目开发中用到的快速排序算法.分享给大家供大家参考,具体如下: 实际上在,做web开发,比较少遇到使用一些算法之类的,毕竟不是做搜索引擎,也不是写底层(比如写个类似于mysql这样的数据库,里面需要自己实现排序算 ...
  • Web2.0编程思想:16条法则
    1.在你开始之前,先定一个简单的目标.无论你是一个Web 2.0应用的创建者还是用户,请清晰的构思你的目标.就像"我需要保存一个书签"或者"我准备帮助人们创建可编辑的.共享的页面"这样的目标,让你保持最基础的需求.很多Web 2.0应用的最初吸引之处就是它的简单,避免并隐藏了那些多余的复杂性.站在创建者的立场,可以想象 ...
  • 提高代码可读性的十大注释技巧分享
    这篇文章主要介绍了提高代码可读性的十大注释技巧,详细分析了编程开发中常用的代码注释方法,需要的朋友可以参考下本文讲述了提高代码可读性的十大注释技巧.分享给大家供大家参考,具体如下: 很多程序员在写代码的时候往往都不注意代码的可读性,让别人在阅读代码时花费更多的时间.其实,只要程序员在写代码的时候,注意为代码加注释,并以合理的格式为代码加注释,这样就方便别人查 ...
猜你喜欢