文章目录
- 前言
- 一、wait() 方法
- 1.wait 做的事情:
- 2.代码实现
- 3.wait结束等待的条件
- 二、notify() 方法
- 1.notify 方法是唤醒等待的线程
- 2.代码演示
- 三、notifyAll() 方法
- 1代码演示:
- 2.理解 notify 和 notifyAl
- 四、wait与sleep区别
前言
由于线程之间是抢占式执行的, 因此线程之间执行的先后顺序难以预知.
但是实际开发中有时候我们希望合理的协调多个线程之间的执行先后顺序.
所以我们得想办法控制线程先后
例如:球场上的每个运动员都是独立的 “执行流” , 可以认为是一个 “线程”.
而完成一个具体的进攻得分动作, 则需要多个运动员相互配合, 按照一定的顺序执行一定的动作, 线
程1 先 “传球” , 线程2 才能 “扣篮”.
为了完成这个协调工作,主要涉及到三个办法
- wait() / wait(long timeout):让方法进入等待状态(下CPU)
- notify():唤醒在当前对象上等待的线程
- notifyAll() :随机从等待的线程中唤醒一个
tip:这三都是Object类的方法
一、wait() 方法
1.wait 做的事情:
- 使当前执行代码的线程进行等待. (把线程放到等待队列中)
- 释放当前的锁
- 满足一定条件时被唤醒, 重新尝试获取这个锁.
tip:wait要搭配 synchronized 来使用. 脱离 synchronized 使用 wait 会直接抛出异常,且锁对象必须是同一个,如下图
2.代码实现
代码如下
Object object = new Object();Thread t = new Thread(()->{synchronized (object) {System.out.println("wait 前");try {object.wait();//1.解锁 2.进入阻塞等待 3.等待通知唤醒(wait必须在加锁情况)} catch (InterruptedException e) {e.printStackTrace();}System.out.println("wait 后");}});t.start();
3.wait结束等待的条件
- 其他线程调用该对象的 notify 方法.
- wait 等待时间超时 (wait 方法提供一个带有 timeout 参数的版本, 来指定等待时间).
- 其他线程调用该等待线程的 interrupted 方法, 导致 wait 抛出 InterruptedException 异常.
通过上述代码,这样在执行到object.wait()之后就一直等待下去,但是程序肯定不能一直这么等待下去了。这个时候就需要使用到了另外一个方法,唤醒的方法notify()。
二、notify() 方法
1.notify 方法是唤醒等待的线程
- 方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的
其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。 - 如果有多个线程等待,则有线程调度器随机挑选出一个呈 wait 状态的线程。(并没有 “先来后到”)
- 在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行
完,也就是退出同步代码块之后才会释放对象锁 (具体看下面代码)。
2.代码演示
public static void main(String[] args) throws InterruptedException {Object object = new Object();Thread t = new Thread(()->{synchronized (object) {System.out.println("wait 前");try {object.wait(); //1.解锁 2.进入阻塞等待 3.等待通知唤醒(wait必须在加锁情况(需重新获取锁))} catch (InterruptedException e) {e.printStackTrace();}System.out.println("wait 后");}});t.start();Thread.sleep(1000);Thread t1 = new Thread(()->{synchronized (object) {System.out.println("notify 前");object.notify(); //1.解锁 2.进入阻塞等待 3.等待通知唤醒(notify必须在加锁情况)System.out.println("notify 后");}});t1.start();}
执行结果如下
这里第二行和第三行验证了,notify代码块执行完才会释放当前锁,t1线程才会重新获取到该对象的锁,然后继续执行。这也就达到了控制线程的执行顺序
三、notifyAll() 方法
notify方法只是唤醒某一个等待线程. 使用notifyAll方法可以一次唤醒所有的等待线程.
1代码演示:
public void run() {synchronized (locker) {System.out.println("notify 开始");locker.notifyAll();System.out.println("notify 结束");}}
2.理解 notify 和 notifyAl
notify 只唤醒等待队列中的一个线程. 其他线程还是乖乖等着
notifyAll 一下全都唤醒, 需要这些线程重新竞争锁:
四、wait与sleep区别
一个是用于线程之间的通信的,一个是让线程阻塞一段时间,
这两方法设计的初衷不同(即解决的问题不同)
wait解决的是线程之间的顺序控制
sleep单纯是为了让线程休眠一会
区别:
- wait 需要搭配 synchronized 使用. sleep 不需要.
- wait 是 Object 的方法 sleep 是 Thread 的静态方法.