博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java 中的wait & notify
阅读量:5156 次
发布时间:2019-06-13

本文共 3286 字,大约阅读时间需要 10 分钟。

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(
){ obj.wait(); } //do bussiness}

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

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

Throws

IllegalArgumentException:timeout参数为负值

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

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

来个图:

Wait&Notify

总结如下过程:

time:01         WaitThread                          NotifyThreadtime: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.notifytime: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();}

转载于:https://www.cnblogs.com/xiaojintao/p/6358545.html

你可能感兴趣的文章
Shader Overview
查看>>
Reveal 配置与使用
查看>>
Java中反射的学习与理解(一)
查看>>
C语言初学 俩数相除问题
查看>>
B/S和C/S架构的区别
查看>>
[Java] Java record
查看>>
jQuery - 控制元素显示、隐藏、切换、滑动的方法
查看>>
postgresql学习文档
查看>>
Struts2返回JSON数据的具体应用范例
查看>>
js深度克隆对象、数组
查看>>
socket阻塞与非阻塞,同步与异步
查看>>
团队工作第二天
查看>>
System类
查看>>
tableView
查看>>
Happy Great BG-卡精度
查看>>
Xamarin Visual Studio不识别JDK路径
查看>>
菜鸟“抄程序”之道
查看>>
Ubuntu下关闭防火墙
查看>>
TCP/IP 邮件的原理
查看>>
w3m常用快捷键
查看>>