目录
前言
什么是monitor?
为什么sleep在Thread中定义?而wait三种方法在Object类中定义?
notify()/notifyAll()和wait()的使用、诠释和剖析
wait 必须在 synchronized 保护的同步代码中使用
虚假唤醒(spurious wakeup)
notify()和notifyAll()
总结一下wait与Sleep()的区别
接着上文JAVA线程基础看这篇文章就够了写补充:
前言
notify()/notifyAll()和wait()三个方法简单的说是唤醒、唤醒所有、让线程等待,wait()与sleep()是不同的——名称不同效果肯定不同。然而同志们往往只知其然不知其所以然。打开编译器点击notify()/notifyAll()和wait()这三个方法会发现其实官方源码有注释,阅读官方的注释当然能够事半功倍,我猜你一定懒得看英文,就让我这个水过6级英语的笔者来替你翻译。
什么是monitor?
阅读源码注释前我们先了解一下频繁出现的monitor这个词:在JVM的规范中,有这么一些话:
- “在JVM中,每个对象和类在逻辑上都是和一个监视器相关联的” (监视器的来由)
- “为了实现监视器的排他性监视能力,JVM为每一个对象和类都关联一个锁”(监视器与对象锁的关系)
- “锁住了一个对象,就是获得对象相关联的监视器”(监视器与锁对象的关系)
翻译成人话就是:每个对象都有一个称之为monitor监视器的锁,这个锁是对象级别的,只要是对象就有。
为什么sleep在Thread中定义?而wait三种方法在Object类中定义?
上面我们说到,monitor是对象级别的,只要是对象就有。所以notify()/notifyAll()和wait()在Object中定义是合适、正确的。那为什么sleep在Thread中呢?
- sleep()的作用是让sleep方法所在的当前线程暂停运行一段时间,其控制范围是由当前线程决定,也就是说得要是线程才能sleep()。
- 这么看来object中定义的方法受众是对象,而Thread中定义的方法受众是线程,二者的受众范围决定了他们定义在不同的类中。
notify()/notifyAll()和wait()的使用、诠释和剖析
wait 必须在 synchronized 保护的同步代码中使用
* The current thread must own this object's monitor. The thread* releases ownership of this monitor and waits until another thread* notifies threads waiting on this object's monitor to wake up* either
【举手】我看得懂我来翻译:当前线程必须要拥有当前对象的监视器(可以理解为要有这个对象的锁,即在synchronized(obj){...}里面,由obj来调用wait方法)。该线程释放他所拥有的监视器(锁)直到其它线程唤醒该线程的监视器(锁)。对于wait方法的解释就到这里了,且编写wait的作者考虑的了”虚假唤醒“和其它情况。
这里提及了三个事情:
- 使用wait()的前提:拥有当前的monitor(对象锁),即在synchronized代码块里面;
- wait后线程会释放锁(以便被notify拿到锁唤醒),直到被唤醒。
- 因为特殊情况(虚假唤醒等),正确使用它又特别的语法,如下:
虚假唤醒(spurious wakeup)
线程可能在既没有被notify/notifyAll,也没有被中断或者超时的情况下被唤醒,称之为虚假唤醒(spurious wakeup)。
* As in the one argument version, interrupts and spurious wakeups are* possible, and this method should always be used in a loop:* <pre>* synchronized (obj) {* while (<condition does not hold>)* obj.wait();* ... // Perform action appropriate to condition* }* </pre>
【举手】我还是能看懂:当在只有一个参数的版本(一把锁),中止和虚假唤醒是可能存在的,那么这个方法(wait)需要经常使用到一个循环:举例:
- 代码解释:while不断的判断条件是否满足,不满足执行obj.wait()方法,让他不断的wait,wait个够。反正它资源第一次wait就释放了,如果存在虚假唤醒或者不可预测的中止情况,可以马上纠正过来。
notify()和notifyAll()
notify()、notifyAll()唤醒单个或所有使用了wait()方法的线程。他们两个是对应配合着唤醒被wait的线程的,因此前置条件和wait一样,都要在synchronized代码块里面 用。同时需要注意的有,面试题目一般会涉及这里,因为这两个方法的存在,所以唤醒wait()的方法可不止一个哦~
* Wakes up a single thread that is waiting on this object's* monitor. If any threads are waiting on this object, one of them* is chosen to be awakened. The choice is arbitrary and occurs at* the discretion of the implementation.
【举手】没错又是我,除了我还有谁:唤醒正在这个对象的监视器上等待的单个线程。如果有不止一个线程在这个对象上等待,选择其中一个唤醒。这个选择是随机的,具体如何选择由实际实现决定。
- 实际上线程wait会进入该对象的wait队列,notify随机唤醒,但是经常是唤醒等待时间最长的那个(就近原则?)。
* The awakened thread will not be able to proceed until the current* thread relinquishes the lock on this object. The awakened thread will* compete in the usual manner with any other threads that might be* actively competing to synchronize on this object; for example, the* awakened thread enjoys no reliable privilege or disadvantage in being* the next thread to lock this object.
【举手】参考了下搜索引擎翻译,翻译出的语句我反正看不懂,还是要看自己翻译一下:被唤醒的线程将无法继续(接着wait后面马上)运行,直到当前线程放弃对该对象的锁定(用这个锁的线程把锁空出来)。唤醒的线将以平常的方式与任何其他可能主动竞争该锁(sycronized代码块的使用权);例如唤醒的线程在存在时没有可靠的权限或处于劣势锁定此对象的下一个线程。(生涩的很,看下面)
-
wait()方法会马上释放锁,但是notify被唤醒后不会马上获得这个锁。它将按照正常的方式(排序呗,进入就绪状态)与其它在排队使用该锁的对象一起竞争。而且抢到使用权后,它将会一直执行完sycronized代码后才会释放该锁
notifyAll就不解释了,如其名ALL全部唤醒。
总结一下wait与Sleep()的区别
首先我们了解一下几种方法的属于:
- notify()/notifyAll()和wait()三种方法属于Obeject类,而sleep()方法属于Thread类。
我们再来了解一下wait和sleep的区别:
- notify()/notifyAll()和wait()必须要在锁中;
- wait()调用后会释放锁。sleep不会,他会一直占有。
- wait直到唤醒前要一直等待,而sleep就定好时间了。
他们的共同点
- 都能想要interrupt标记位,抛出interruptedException:具体interrupt原理博客;
- 都能阻塞线程。