wait 和 notify方法

   由于线程之间是抢占式执行的, 因此线程之间执行的先后顺序难以预知. 但是实际开发中有时候我们希望合理的协调多个线程之间的执行先后顺序.

完成这个协调工作 , 主要涉及到三个方法
  • wait() / wait(long timeout): 让当前线程进入等待状态.
  • notify() / notifyAll(): 唤醒在当前对象上等待的线程.
    注意: wait, notify, notifyAll 都是 Object 类的方法.      

1.1 wait()方法

 wait 做的事情:

  • 使当前执行代码的线程进行等待. (把线程放到等待队列中)
  • 释放当前的锁
  • 满足一定条件时被唤醒, 重新尝试获取这个锁. wait 要搭配 synchronized 来使用. 脱离 synchronized 使用 wait 会直接抛出异常.

wait 结束等待的条件:

  • 其他线程调用该对象的 notify 方法.
  • wait 等待时间超时 (wait 方法提供一个带有 timeout 参数的版本, 来指定等待时间).
  • 其他线程调用该等待线程的 interrupted 方法, 导致 wait 抛出 InterruptedException 异常.

代码示例:  

public static void main(String[] args) throws InterruptedException {
    Object object = new Object();
    synchronized (object) {
        System.out.println("等待中");
        object.wait();
        System.out.println("等待结束");
   }
}

         这样在执行到object.wait()之后就一直等待下去,那么程序肯定不能一直这么等待下去了。这个时候就 需要使用到了另外一个方法唤醒的方法notify()。

1.2 notify()方法 

 notify 方法是唤醒等待的线程.

  • 方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的 其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。
  • 如果有多个线程等待,则有线程调度器随机挑选出一个呈 wait 状态的线程。(并没有 "先来后到")
  • notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行 完,也就是退出同步代码块之后才会释放对象锁。
package thread;

public class Demo20 {
    public static void main(String[] args) {
        Object object = new Object();

        Thread t1 = new Thread(() -> {
            synchronized (object) {
                System.out.println("wait 之前");
                try {
                    object.wait();
           //         object.wait(3000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("wait 之后");
            }
        });

        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized (object) {
                System.out.println("进行通知");
                object.notify();
            }
        });

        t2.start();       t1.start();
    }
}

运行结果: 

 

1.3notifyAll方法

notify()和notifyAll()方法都用来唤醒wait()阻塞方法。

        但是notify()方法一次只能某一个等待线程,而notifyAll()方法一次调用可以唤醒全部的相同锁对象的wait()方法。notifyAll()方法虽然能一次唤醒多个线程,但是多个线程要竞争锁,并不是同步执行,也是依次执行。

1.4wait()和sleep()对比

        前者是用于线程之间的通信的,后者仅是让线程阻塞一段时间。唯一的相同点就是都可以让线程放弃执行一段时间。

不同点:

wait()方法需要搭配synchronized锁使用,而sleep()则不用;
wait()是Object类的普通方法,而sleep()Thread类的静态方法。