wait(),notify(),notifyAll()等方法介绍
在Object.java中,定义了wait(),notify(),和notifyAll()等接口。wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。而notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程,notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程
Object类中关于等待/唤醒的API详细信息如下
notify() 唤醒在此对象监视器上等待的单个线程
notifyAll()唤醒在此对象监视器上等待的所有线程
wait() 让当前线程处于等待(阻塞)状态,直到其他线程调用此对象的notify()方法或notifyAll()方法,当前线程被唤醒(进入"就绪状态")
wait(long timeout)让当前线程处于等待(阻塞)状态,直到其他线程调用此对象的notify()方法或notifyAll()方法,当前线程被唤醒(进入"就绪状态")或者超过我们指定的时间量 当前线程被唤醒(进入就绪状态)
wait(long timeout,int nanos)让当前线程处于等待(阻塞)状态,直到其他线程调用此对象的notify()方法或notifyAll()方法,当前线程被唤醒(进入"就绪状态")或者超过我们指定的时间量 或者其他线程中断当前线程 当前线程被唤醒(进入就绪状态)
2 wait()和notify()示例
package com.tuhu.filt.javadatathread;
public class ThreadA extends Thread{
public ThreadA(String name){
super(name);
}
public void run(){
synchronized (this){
System.out.println(
Thread.currentThread().getName()
+" call notify"
);
notify();
}
}
}
class WaitTest{
public static void main(String[] args) {
ThreadA t1 = new ThreadA("t1");
synchronized (t1){
try{
System.out.println(
Thread.currentThread().getName()
+" start t1"
);
t1.start();
/**
* 下面这个输出语句也是main线程调用的,调用了之后执行run方法才
* 出现那个t1 也就是t0
*/
System.out.println(
Thread.currentThread().getName()
+" wait()"
);
t1.wait();
/**
* t1阻塞之后 又把main给唤醒了 哈哈哈
*/
System.out.println(
Thread.currentThread().getName()
+" continue"
);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
我们来说明下主线程和线程t1的流程
3 wait(long timeout)和notify()
下面演示wait(long timeout)在超时情况下,线程被唤醒的情况
package com.tuhu.filt.javadatathread;
public class ThreadB extends Thread {
public ThreadB(String name){
super(name);
}
public void run(){
System.out.println(Thread.currentThread().getName()
+" run"
);
while(true){
;
}
}
}
class WaitTimeoutTest{
public static void main(String[] args) {
ThreadB t1 = new ThreadB("t1");
synchronized (t1){
try{
System.out.println(
Thread.currentThread().getName()
+" start t1"
);
t1.start();
System.out.println(
Thread.currentThread().getName()
+" call wait"
);
t1.wait(3000);
System.out.println(
Thread.currentThread().getName()
+" continue"
);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
4 wait()和notifyAll()
public class NotifyAllTest {
private static Object obj = new Object();
public static void main(String[] args) {
ThreadA t1 = new ThreadA("t1");
ThreadA t2 = new ThreadA("t2");
ThreadA t3 = new ThreadA("t3");
t1.start();
t2.start();
t3.start();
try {
System.out.println(Thread.currentThread().getName()+" sleep(3000)");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(obj) {
// 主线程等待唤醒。
System.out.println(Thread.currentThread().getName()+" notifyAll()");
obj.notifyAll();
}
}
static class ThreadA extends Thread{
public ThreadA(String name){
super(name);
}
public void run() {
synchronized (obj) {
try {
// 打印输出结果
System.out.println(Thread.currentThread().getName() + " wait");
// 唤醒当前的wait线程
obj.wait();
// 打印输出结果
System.out.println(Thread.currentThread().getName() + " continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
t1 wait
main sleep(3000)
t3 wait
t2 wait
main notifyAll()
t2 continue
t3 continue
t1 continue
结果说明:
参考下面的流程图。
(01) 主线程中新建并且启动了3个线程"t1", "t2"和"t3"。
(02) 主线程通过sleep(3000)休眠3秒。在主线程休眠3秒的过程中,我们假设"t1", "t2"和"t3"这3个线程都运行了。以"t1"为例,当它运行的时候,它会执行obj.wait()等待其它线程通过notify()或额nofityAll()来唤醒它;相同的道理,"t2"和"t3"也会等待其它线程通过nofity()或nofityAll()来唤醒它们。
(03) 主线程休眠3秒之后,接着运行。执行 obj.notifyAll() 唤醒obj上的等待线程,即唤醒"t1", "t2"和"t3"这3个线程。 紧接着,主线程的synchronized(obj)运行完毕之后,主线程释放“obj锁”。这样,"t1", "t2"和"t3"就可以获取“obj锁”而继续运行了!
5 为什么notify() wait()等函数定义在object而不是在Thread中
object wait() notify() 和synchronized一样 会对对象同步锁 进行操作
wait()会让当前线程进入等待状态 进入等待状态 所以线程应该释放它所持有的"同步锁",否则其他线程获取不到该"同步锁"而无法运行
释放掉同步锁之后,等待线程可以被notify()唤醒
请思考一个问题:notify()是依据什么唤醒等待线程的?或者说,wait()等待线程和notify()之间是通过什么关联起来的?答案是:依据“对象的同步锁”。
负责唤醒等待线程的那个线程(我们称为“唤醒线程”),它只有在获取“该对象的同步锁”(这里的同步锁必须和等待线程的同步锁是同一个),并且调用notify()或notifyAll()方法之后,才能唤醒等待线程。虽然,等待线程被唤醒;但是,它不能立刻执行,因为唤醒线程还持有“该对象的同步锁”。必须等到唤醒线程释放了“对象的同步锁”之后,等待线程才能获取到“对象的同步锁”进而继续运行。
总之,notify(), wait()依赖于“同步锁”,而“同步锁”是对象锁持有,并且每个对象有且仅有一个!这就是为什么notify(), wait()等函数定义在Object类,而不是Thread类中的原因。