多线程系列05-线程等待与唤醒

2019-07-22  本文已影响0人  Sandy_678f

线程的等待与唤醒
在Object.java中,定义了wait(), notify()和notifyAll()等接口。wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。而notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。
Object类中关于等待/唤醒的API详细信息如下:

  1. notify():随机唤醒在此对象监视器上等待的单个线程。
  2. notifyAll():唤醒在此对象监视器上等待的所有线程。
  3. wait():让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)。
  4. wait(long timeout):让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。
  5. wait(long timeout, int nanos):让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量”,当前线程被唤醒(进入“就绪状态”)。

示例1:wait() 和 notify()方法

public class ThreadA extends Thread{

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

    public void run(){
        synchronized (this){
            System.out.println(Thread.currentThread().getName() + " call notify()");
            //唤醒当前的wait线程
            notify();
        }
        yield();
        System.out.println(Thread.currentThread().getName() + " is running.");
    }


    public static void main(String[] args) {
        ThreadA t1 = new ThreadA("t1");
        ThreadA t2 = new ThreadA("t2");

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

                //主线程等待t1通过notify()唤醒
                System.out.println(Thread.currentThread().getName() + " wait()");
                t1.wait();

                System.out.println(Thread.currentThread().getName() + " continue");

            }catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

运行结果:

main start t1
main start t2
main wait()
t2 call notify()
t1 call notify()
main continue
t2 is running.
t1 is running.

几点说明:

  1. wait()是让“当前线程”等待
  2. wait() 相当于 wait(0)方法,表示无限等待
  3. “当前线程”在调用wait()方法时,必须拥有对象的同步锁,该线程调用完wait()方法后,释放该锁

==========
示例2:wait(long timeout) 方法

public class ThreadB extends Thread{

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

    public void run(){
        System.out.println(Thread.currentThread().getName() + "  is running");
        //无限循环
        while(true)
            ;
    }

    public static void main(String[] args) {
        ThreadB threadB = new ThreadB("threadB");

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

                //主线程等待threadB通过notify(),或notifyAll(),或超过3000ms; 唤醒
                System.out.println(Thread.currentThread().getName() + " call wait");
                threadB.wait(3000);

                System.out.println(Thread.currentThread().getName() + " continue");
                

            } catch (InterruptedException e) {
                e.printStackTrace();
            }


        }
    }
}

运行结果:

main start threadB
main call wait
threadB is running
main continue

Process finished with exit code 130 (interrupted by signal 2: SIGINT)

==========
示例3:wait() 和 notifyAll()方法

public class NotifyAllTest {

    private static Object obj = new Object();

    static class ThreadA extends Thread{
        public  ThreadA(String name){
            super(name);
        }

        public void run(){
            synchronized (obj){
                try{
                    System.out.println(Thread.currentThread().getName() + " wait");

                    //当前线程等待唤醒
                    obj.wait();

                    System.out.println(Thread.currentThread().getName() + " continue");

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    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();
        }

    }

}

运行结果:

t1 wait
main sleep(3000)
t3 wait
t2 wait
main notifyAll()
t2 continue
t3 continue
t1 continue

几点说明:
负责唤醒等待线程的那个线程(我们称为“唤醒线程”),它只有在获取“该对象的同步锁”(这里的同步锁必须和等待线程的同步锁是同一个),并且调用notify()或notifyAll()方法之后,才能唤醒等待线程。
notify(), wait()依赖于“同步锁”,而“同步锁”是对象锁持有,并且每个对象有且仅有一个!这就是为什么notify(), wait()等函数定义在Object类,而不是Thread类中的原因。

上一篇下一篇

猜你喜欢

热点阅读