thinking in java (三十九) ----- 多线程05之线程等待和唤醒

wait() notify()notifyAll()简介

这三个方法都是定义在Object里面,wait()的作用是让线程进入等待状态,同时wait也会释放当前线程所持有的锁,而notify和notifyAll的作用是唤醒当前对象上的等待线程,notify是唤醒单个线程,而notifyAll 是唤醒所有线程。

Object中关于等待唤醒的API详细信息如下:

notify() 唤醒在此对象监视器上等待的单个线程

notifyAll()   -- 唤醒在此对象监视器上等待的所有线程。

wait() -让当前线程处于等待(阻塞)状态,直到其他线程调用此对象的notifyall,notify方法,当前线程处于就绪状态

wait(long time)-让当前线程处于等待(阻塞)状态,直到其他线程调用此对象的notifyall,notify方法,或者超乎指定时间time,当前线程处于就绪状态

wait(long timeout, int nanos)  -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量”,当前线程被唤醒(进入“就绪状态”)。

wait()  notify()示例

package test;
//WaitTest.java的源码
class MyThread1 extends Thread{  
	 public MyThread1(String name) {
	     super(name);
	 }

	 public void run(){
	     System.out.println(Thread.currentThread().getName()+" is running");
	 } 
	}; 
public class WaitTest {

 public static void main(String[] args) {

	 MyThread1 t1 = new MyThread1("t1");

     synchronized(t1) {
         
             // 启动“线程t1”
             System.out.println(Thread.currentThread().getName()+" start t1");
             t1.start();
//             t1.run();

             // 主线程等待t1通过notify()唤醒。
             System.out.println(Thread.currentThread().getName()+" wait()");
             try {
				t1.wait();
				
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
           
             System.out.println(Thread.currentThread().getName()+" continue");
         
     }
 }
}

运行结果:

main start t1
main wait()
t1 call notify()
main continue

结果说明;

1,注意图中“主线程”代表的是main线程,“线程t1”代表WaitTest中启动的线程t1,而锁代表t1这个对象的同步锁

2,主线程通过通过 new ThreadA("t1") 新建“线程t1”。随后通过synchronized(t1)获取t1的同步锁,然后通过t1.start()将线程置于就绪状态,(并不是start就能获取CPU启动,在本案例中,此时正在运行main线程,需要等到下面的那句wait以后,main线程进入阻塞状态,t1线程才能开始执行run方法)

3,主线程执行t1.wait()释放t1对象的锁,并且进入等待(阻塞)状态,等待t1对象的线程通过notify 或者notifyAll进行唤醒,(本案例中去除掉notify,程序运行结果也一样,因为只有两个线程,t1和main,t1执行到notify了,相当于t1也执行完了它的run体,它就dead状态了,那锁就会被剩余的线程来争抢,因为剩余的只有main,那肯定就是它获得锁了,所以加不加notify都无所谓,反正main都是能够独占这个锁的。)

4,线程t1运行之后,通过synchronized(this)获取当前对象的锁,接着调用notify()唤醒“当前对象上的等待线程”,也就是唤醒主线程

5,线程t1运行完毕以后,释放当前对象的锁,紧接着,主线程获取t1对象的锁,然后接着运行

问题来了:t1.wait()应该是让“线程t1”等待;但是,为什么却是让“主线程main”等待了呢?

注意:jdk的解释中,说wait()的作用是让“当前线程”等待,而“当前线程”是指正在cpu上运行的线程!
这也意味着,虽然t1.wait()是通过“线程t1”调用的wait()方法,但是调用t1.wait()的地方是在“主线程main”中。而主线程必须是“当前线程”,也就是运行状态,才可以执行t1.wait()。所以,此时的“当前线程”是“主线程main”!因此,t1.wait()是让“主线程”等待,而不是“线程t1”

wait(long time)与notify()

wait(long timeout)会让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。
下面的示例就是演示wait(long timeout)在超时情况下,线程被唤醒的情况。

// WaitTimeoutTest.java的源码
class ThreadA extends Thread{

    public ThreadA(String name) {
        super(name);
    }

    public void run() {
        System.out.println(Thread.currentThread().getName() + " run ");
        // 死循环,不断运行。
        while(true)
            ;
    }
}

public class WaitTimeoutTest {

    public static void main(String[] args) {

        ThreadA t1 = new ThreadA("t1");

        synchronized(t1) {
            try {
                // 启动“线程t1”
                System.out.println(Thread.currentThread().getName() + " start t1");
                t1.start();

                // 主线程等待t1通过notify()唤醒 或 notifyAll()唤醒,或超过3000ms延时;然后才被唤醒。
                System.out.println(Thread.currentThread().getName() + " call wait ");
                t1.wait(3000);

                System.out.println(Thread.currentThread().getName() + " continue");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果:

main start t1
main call wait 
t1 run 
main continue
结果说明

其实和前一个程序结果一样,只是main线程的阻塞时间多了3000ms,

wait()和notifyAll()

通过前面的示例,我们知道 notify() 可以唤醒在此对象监视器上等待的单个线程。
下面,我们通过示例演示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锁”而继续运行了!

为什么notify(),wait()等方法定义在Object中

Object中的notify,wait方法都会和synchronized一样,会对对象的同步锁进行操作。

wait会让当前线程等待,要让线程进入等待,线程就应该释放他的同步锁。否则其他线程获取不到同步锁也运行不起来

等待线程会被notify或者notifyAll唤醒,那么这两个方法唤醒线程依靠的仍然是同步锁。

负责唤醒线程的那个线程(我们称之为唤醒线程),它只有在获取“对象的同步锁”(这里的同步锁和等待线程的同步锁是同一个),并且调用notify和notifyAll之后,才能唤醒等待线程。在等待线程被唤醒后,也不会立马运行,因为唤醒了线程还持有同步锁才能运行。

、总之,这些方法依赖于同步锁,而同步锁是对象持有的,并且每个对象有且只有一个,这就是为什么这些方法定义在Object类,而不是在Thread类中

 

 

 

原文:http://www.cnblogs.com/skywang12345/p/3479224.html

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值