博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
为什么wait()和notify()需要搭配synchonized关键字使用
阅读量:4125 次
发布时间:2019-05-25

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

理解此问题先修知识:

  • synchronized 的含义:

    • Java中每一个对象都可以成为一个监视器(Monitor), 该Monitor由一个锁(lock), 一个等待队列(waiting queue ), 一个入口队列( entry queue).
    • 对于一个对象的方法, 如果没有synchronized关键字, 该方法可以被任意数量的线程,在任意时刻调用。
    • 对于添加了synchronized关键字的方法,任意时刻只能被唯一的一个获得了对象实例锁的线程调用。
    • synchronized用于实现多线程的同步操作
  • wait()功用

    • wait(), notify(), notifyAll()synchonized 需要搭配使用, 用于线程同步
    • wait()总是在一个循环中被调用,挂起当前线程来等待一个条件的成立。 Wait调用会一直等到其他线程调用notifyAll()时才返回。
    • 当一个线程在执行synchronized 的方法内部,调用了wait()后, 该线程会释放该对象的锁, 然后该线程会被添加到该对象的等待队列中(waiting queue), 只要该线程在等待队列中, 就会一直处于闲置状态, 不会被调度执行。 要注意wait()方法会强迫线程先进行释放锁操作,所以在调用wait()时, 该线程必须已经获得锁,否则会抛出异常。由于wait()在synchonized的方法内部被执行, 锁一定已经获得, 就不会抛出异常了。
      这里写图片描述
  • notify()的功用

    • wait(), notify(), notifyAll()synchonized 需要搭配使用, 用于线程同步
    • 当一个线程调用一个对象的notify()方法时, 调度器会从所有处于该对象等待队列(waiting queue)的线程中取出任意一个线程, 将其添加到入口队列( entry queue) 中. 然后在入口队列中的多个线程就会竞争对象的锁, 得到锁的线程就可以继续执行。 如果等待队列中(waiting queue)没有线程, notify()方法不会产生任何作用
    • notifyAll()notify()工作机制一样, 区别在于notifyAll()会将等待队列(waiting queue)中所有的线程都添加到入口队列中(entry queue)
    • 注意, notifyAll()notify()更加常用, 因为notify()方法只会唤起一个线程, 且无法指定唤醒哪一个线程,所以只有在多个执行相同任务的线程在并发运行时, 我们不关心哪一个线程被唤醒时,才会使用notify()

为什么wait()notify()notifyAll()需要搭配synchronized关键字使用

  • 从语义角度来讲, 一个线程调用了wait()之后, 必然需要由另外一个线程调用notify()来唤醒该线程, 所以本质上, wait()notify()的成对使用, 是一种线程间的通信手段。
  • 进一步分析, wait() 操作的调用必然是在等待某种条件的成立, 而条件的成立必然是由其他的线程来完成的。 所以实际上, 我们调用 wait() 的时候, 实际上希望达到如下的效果
// 线程A 的代码while(!condition){
// 不能使用 if , 因为存在一些特殊情况, 使得线程没有收到 notify 时也能退出等待状态 wait();}// do something
// 线程 B 的代码if(!condition){
// do something ... condition = true; notify();}

现在考虑, 如果wait()notify() 的操作没有相应的同步机制, 则会发生如下情况(Lost Wake-Up

  1. 【线程A】 进入了 while 循环后(通过了 !condition 判断条件, 但尚未执行 wait 方法), CPU 时间片耗尽, CPU 开始执行线程B的代码
  2. 【线程B】 执行完毕了 condition = true; notify(); 的操作, 此时【线程A】的 wait() 操作尚未被执行, notify() 操作没有产生任何效果
  3. 【线程A】执行wait() 操作, 进入等待状态,如果没有额外的 notify() 操作, 该线程将持续在 condition = true 的情形下, 持续处于等待状态得不到执行。

由此看出, 在使用 wait() 和 notify() 这种会挂起线程的操作时, 我们需要一种同步机制保证, condition 的检查与 wait() 操作, 以及 condition 的更新与 notify() 是互斥的。

  • 那是否简单的将之前的代码包裹在一个 synchronized 代码块中就可以满足需求呢? 像下面这样。
// 线程A 的代码synchronized(obj_A){
while(!condition){
wait(); } // do something }
// 线程 B 的代码synchronized(obj_A){
if(!condition){
// do something ... condition = true; notify(); }}
  • 乍一看, 上述的代码可以解决问题, 但是仔细分析一下, 由于wait() 操作会挂起当前线程, 那么必然需要在挂起前释放掉 obj_A 的锁, 但如果 obj_A 允许是任意对象, wait() 函数作为一个没有参数输入的方法,无从得知应该释放哪个对象的锁 。于是很自然的, 语法就会被设计成 java 现在的样子。即基于对象的 wait()notify() 的调用, 必须先获得该对象的锁。

  • 正确的用法示例如下

// 线程 A 的代码synchronized(obj_A){
while(!condition){
obj_A.wait(); } // do something }
// 线程 B 的代码synchronized(obj_A){
if(!condition){
// do something ... condition = true; obj_A.notify(); }}
你可能感兴趣的文章
如何运行从网上下载的iWatch项目详细步骤.
查看>>
X-code7 beta error: warning: Is a directory
查看>>
Error: An App ID with identifier "*****" is not avaliable. Please enter a different string.
查看>>
X-code beta 开发iWatch项目,运行没有错误,但是某些操作一点就崩,而且找不错误的原因场景一
查看>>
Xcode 报错: Extra argument in call
查看>>
iTunes Connect 上传APP报错: Communication error. please use diagnostic mode to check connectivity.
查看>>
#import <Cocoa/Cocoa.h> 报错 Lexical or Preprocessor Issue 'Cocoa/Cocoa.h' file not found
查看>>
`MQTTClient (~> 0.2.6)` required by `Podfile`
查看>>
X-Code 报错 ld: library not found for -lAFNetworking
查看>>
Bitcode
查看>>
If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
查看>>
3.5 YOLO9000: Better,Faster,Stronger(YOLO9000:更好,更快,更强)
查看>>
iOS菜鸟学习--如何避免两个按钮同时响应
查看>>
How to access the keys in dictionary in object-c
查看>>
iOS菜鸟学习—— NSSortDescriptor的使用
查看>>
hdu 3787 hdoj 3787
查看>>
hdu 3790 hdoj 3790
查看>>
hdu 3789 hdoj 3789
查看>>
hdu 3788 hdoj 3788
查看>>
zju 1003 zoj 1003
查看>>