• java 中的wait & notify


    Wait&Notify

    以下内容来自《Java并发编程的艺术》,4.3.2 等待/通知机制

    线程A等待某一个变量_v_满足某个条件,而线程B会在某个不确定的时刻修改_v_,以使其满足条件,那么线程A所要做的无非以下两种操作:

    • 轮询变量_v_,直到_v_满足条件,A继续完成它的工作
    • 每隔一段时间检查变量_v_,这期间可能休眠,也可能做其它的事

    可以看到,以上两种操作刚好是矛盾的,第一种会一直占用CPU资源,而且是在浪费,但是可以保证实时性,即当_v_满足条件,它立刻就会知道。第二种会降低CPU的开销,或者减少浪费,但是很难保证实时性。

    Java内置的等待/通知机制能够很好的解决这个矛盾并实现所需的功能。

    等待/通知相关的方法是任意Java对象都具有的,这些方法定义在java.lang.Object

    Method Desc
    notify()
    notifyAll()
    wait()
    wait(long)
    wait(long, int)

    JDK中关于这几个方法的解释:

    public final native void notify()

    唤醒一个在该对象监视器上等待的线程。如果有很多线程在该对象上等待,就挑选其中的一个。选择是任意的,在具体实现的时候自由裁决。一个线程通过调用该对象的wait方法,开始在该对象的监视器锁上等待(前提是该线程持有该监视器锁)。

    被唤醒的线程并不是立即开始执行,要等到当前线程释放掉该对象锁(或从同步块中出来)。被唤醒的线程将以常规的方式继续在该对象同步块中完成它的工作。例如,唤醒的线程对于接下来要获得锁的线程没有优先级。

    该方法应该被持有该对象监视器的线程调用。有三种方式使一个线程拥有对象监视器:

    • 执行该对象的同步方法
    • 进入同步代码块
    • 对于类对象,执行类的静态同步方法

    同一时刻只有一个线程能持有对象监视器。

    Throws:IllegalMonitorStateException 如果当前线程不持有该对象的监视器

    public final native void notifyAll()

    唤醒所有在该对象锁上等待的线程。即所有线程都开始准备进入该对象监视器。

    public final native void wait(long timeout) throws InterruptedException

    引起当前线程等待,直到其它线程调用该对象的notify()notifyAll()方法,或者在特定的超时时间之后。

    当前线程需要持有该对象的监视器。

    该方法会引起当前线程(如线程T),使其放弃持有的对象锁,将自身放入该对象的等待集合。线程T不能被线程调度器调度,会保持挂起直到如下发生:

    • 某个其它线程调用该对象的notify()方法,线程T可能会被随机选中,并唤醒
    • 其它线程调用该对象的notifyAll()方法
    • 其它线程中断线程T
    • 指定的超时时间到了。如果超时时间设为0,那么超时时间无效,结果等价于wait()方法

    然后,线程T会从该对象的等待集合中移除,然后再次可被调度。然后它会再次与其它线程竞争进入对象监视器的机会,一旦它取得了控制权,它再次回到它睡眠前的状态,就好像没有发上一样,继续执行。

    线程可以不需要通知,中断或超时而被唤醒,这里叫做伪唤醒。尽管实际很少发生,程序必须小心测试唤醒线程的条件,而且当条件不满足的时候,换句话说,应该让wait()方法在一个循环中执行,直到条件满足,就像这样:

    synchronized(obj){
      while(<condition is not satisfied>){
        obj.wait();
      }
      //do bussiness
    }
    

    如果当前线程在等待之前,或正在等待的时候被中断了,就抛出InterruptedException异常。这个异常会知道线程再次获得锁的时候才抛出。

    当该线程在等待的时候,它还是有可能持有其它的对象锁!这里一定要注意。

    Throws#####

    IllegalArgumentException:timeout参数为负值

    IllegalMonitorStateException:如果当前线程没有持有该对象锁

    InterruptedException:如果其它线程在该线程开始等待或正在等待的时候中断线程,中断标志位会清除。

    来个图:

    Wait&Notify

    总结如下过程:

    time:01			WaitThread							NotifyThread
    time:02			进入监视器							...
    time:03			...									进入监视器
    time:04			...									...	
    time:05			进入成功							 ...
    time:06			...									进入失败
    time:07			...									...
    time:08			...									进入同步队列
    time:09			Object.wait()						 ...
    time:10			...									从同步队列出来
    time:11			进入等待队列						    进入监视器
    time:12			...									...
    time:13			...									进入成功
    time:14			...									...
    time:15			...									Object.notify
    time:16			...									...
    time:17			从等待队列进入同步队列				     ...
    time:18			...									出监视器
    time:19			进入监视器							...
    time:20			...									结束
    time:21			进入成功
    time:22			...
    time:23			...
    time:24			结束
    

    过程就是这么个过程,纵向为时间。

    等待/通知最佳实践

    等待方遵循原则:

    • 获取对象锁
    • 如果条件不满足,那么调用对象的wait()方法,被通知后仍要检查条件
    • 条件满足就执行对应的逻辑

    对应的伪代码:

    synchronized(obj){
      while(<条件不满足>){
        obj.wait();
      }
      //执行相应逻辑
    }
    

    通知方伪代码:

    • 获得对象锁
    • 改变条件
    • 通知所有在该对象的线程(可能会通知到业务不相关的线程)

    对应伪代码:

    sychronized(obj){
      //改变条件
      obj.notifyAll();
    }
    
  • 相关阅读:
    IDEA连接 Oracle数据库
    什么是混合云备份
    什么是阿里云ACA认证
    什么是阿里云ACE认证
    什么是轻量应用服务器
    什么是时序时空数据库TSDB
    什么是数据管理DMS
    什么是分析型数据库PostgreSQL版
    阿里云多端小程序
    阿里云云计算ACP专业认证考试
  • 原文地址:https://www.cnblogs.com/xiaojintao/p/6358545.html
Copyright © 2020-2023  润新知