等待/通知机制

什么是等待通知机制

在单线程中,要执行的操作需要满足一定的条件才能执行,可以把这个操作放在if语句块中。
在多线程中,可能A线程的条件没有满足只是暂时的,稍后可能B线程会更新使得A线程的条件得到满足。所以可以将A线程暂停,直到它的条件得到满足后再将A线程唤醒。伪代码如下:

atomics{ //原子操作
   while(条件不成立){
      等待
    }
     条件满足后,继续执行操作。
}

等待/通知机制的实现

Object类中的wait()方法可以使执行当前代码的线程等待,暂停执行,直到接到通知或被中断为止。
注意:
1.wait()方法只能在同步代码块中由锁对象调用。
2.调用wait()方法,当前线程会释放锁。
伪代码如下:
//在调用wait()方法之前必须获得对象锁

     synchronized(锁对象){
               while(条件不满足){
                    锁对象.wait();   //通过锁对象调用wait()方法暂停线程,会释放锁对象。
                }
               //条件满足,线程向下执行。
        }

Object类中的notify()可以唤醒线程,该方法也必须在同步代码块中由锁对象调用。没有使用锁对象调用会抛出illegalMonitorStateExepition异常。如果有多个等待线程,notify()只能唤醒其中的一个。在同步代码块中调用notily()方法后,并不会立即释放锁对象,需要等当前同步代码块执行完后才会释放锁对象,一般将notify()方法放在同步代码块的最后。伪代码如下:

 synchronized(锁对象){
            //修改保护条件代码
           锁对象.notify(); //唤醒其他线程;
}

测试代码:

static String lock="lock";

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock){
                    System.out.println("线程1开始等待"+" "+System.currentTimeMillis());
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程1已经唤醒"+" "+System.currentTimeMillis());
                }

            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock){
                    System.out.println("线程2开始唤醒"+" "+System.currentTimeMillis());
                     lock.notify();
                    System.out.println("线程2结束唤醒"+" "+System.currentTimeMillis());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });


          t1.start();
          Thread.sleep(1000);
          t2.start();
    }

interrupt()方***中断线程的wait()等待

当线程处于wait()等待状态时,调用线程对象的interrupt()方***中断线程的等待状态,会产生InterruptedException异常。
图片说明
结果:
图片说明

notify()与notifyAll的区别

notify()一次只能唤醒一个线程,如果有多个线程需要唤醒,notify()只能随机的唤醒其中的一个。notifyAll()可以唤醒所有的线程。

 final static Object object=new Object();
    public static void main(String[] args) throws InterruptedException {

        Thread thread01= new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println(Thread.currentThread().getName() + "   开始等待");
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "    等待结束");
                }
            }
        });
        Thread thread02= new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println(Thread.currentThread().getName() + "   开始等待");
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "    等待结束");
                }
            }
        });
        Thread thread03= new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println(Thread.currentThread().getName() + "   开始等待");
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "    等待结束");
                }
            }
        });

        Thread thread04= new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println(Thread.currentThread().getName() + "   开始唤醒");
                        object.notifyAll();    
                    System.out.println(Thread.currentThread().getName() + "   唤醒结束");
                }
            }
        });


        thread01.start();
        thread02.start();
        thread03.start();
        Thread.sleep(5000);
        thread04.start();
    }

全部唤醒结果:
图片说明

wait(long) 方法

如果在long时长内线程没有被唤醒,那么这个线程会自动唤醒。

通知过早

当线程等待唤醒之前就执行了唤醒操作,可能就会影响程序正常的工作流程。