目录
前言:
由于线程之间是抢占式执行的,因此线程之间的执行先后顺序总是难以预知的。但是实际开发中我们总是希望能够合理的协调多个线程之间的执行先后顺序。
完成多线程协调的这个工作,主要涉及到四个方法:
- wait()/wait(long timeout):让当前的线程进入等待状态;
- notify()/notifyAll():唤醒当前的对象上等待的那个线程;
注意:wait()、wait(long timeout)、notify()、notifyAll()都是Object类的方法。
序列:多线程 - 007
1.wait()方法
1.1wait()使用
首先,wait()方法需要搭配synchronized使用,放到synchronized锁中,否则程序会直接报异常。
wait()在执行时需要做的事情:
- 使当前执行代码的线程进行等待(把线程放到等待的队列中);
- 释放当前的锁;
- 当等待被唤醒时,重新尝试获得这个锁;
wait()结束等待的条件:
- 其他的线程调用该对象的notify()方法;
- wait()的等待时间超时(wait()提供一个带有timeout参数的版本,表示最大的等待时间);
wait()无参方法表示一直等待,直到触发第一个结束等待条件;wait(long timeout)有参方法,触发结束等待的两个条件都可以结束等待。
wait()的使用语法:
synchronized (object){
System.out.println("wait之前");
//把wait()放到synchronized里边来调用,确保wait()拿到了锁,才能去释放锁
object.wait();//wait()会阻塞线程,直到notify()来唤醒线程,线程才能继续运行
System.out.println("wait之后");
}
1.2代码实例
以下代码thread线程会每隔1秒循环打印一次 “线程运行中”,一共5次,但只打印出前3次,因为thread线程第三秒时会触发wait等待,此时线程会触发等待,直到别的线程调用该对象的notify()方法,才会唤醒thread线程。
public class Main {
public static void main(String[] args) throws InterruptedException {
Object object = new Object();//创建锁对象和wait对象
Thread thread = new Thread(()->{
for (int i = 1; i < 6; i++) {
try {
Thread.sleep(1000);//设置时间为1秒,每隔1秒循环打印一次
System.out.println("线程运行中 " + i);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (i == 3){
synchronized (object){//wait方法必须要搭配synchronized锁使用
try {
object.wait();//运行三秒后线程开始等待,但并没有停止运行
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
});
thread.start();//开始运行线程
}
}
运行结果如下,这样在执行到object.wait()时程序就会停止运行,一直等待下去。但是程序肯定不能就一直等待下去,所以就需要用到notify()方法进行唤醒。
1.3wait()和sleep()对比
其实理论上wait()和sleep()是完全没有可比性的,因为前者是用于线程之间的通信的,后者仅是让线程阻塞一段时间。
唯一的相同点就是都可以让线程放弃执行一段时间。
不同点:
- wait()方法需要搭配synchronized锁使用,而sleep()则不用;
- wait()是Object类的普通方法,而sleep()Thread类的静态方法。
2.notify()/notifyAll()方法
2.1notify()使用
notify()方法是用来唤醒等待的线程,但只能唤醒等待队列中的一个线程。
在唤醒指定的线程的wait()方法时,需要与被唤醒的wait()方法的锁对象保持一致,notify()方法也需要搭配synchronized锁进行使用。
notify()的使用示例:
synchronized (object){
object.notify();//唤醒object对象对应的wait方法
}
2.2代码实例
接着上文的代码实例,这次代码加入notify()方法,让线程thread等待两秒后,又会接着运行,直到结束为止。
public class Main {
public static void main(String[] args) throws InterruptedException {
Object object = new Object();//创建锁对象和wait对象
Thread thread = new Thread(()->{
for (int i = 1; i < 6; i++) {
try {
Thread.sleep(1000);//设置时间为1秒,每隔1秒循环打印一次
System.out.println("线程运行中 " + i);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (i == 3){
synchronized (object){//wait方法必须要搭配synchronized锁使用
try {
object.wait();//运行三秒后线程开始等待,等待2秒后又开始继续运行,直到结束
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
});
thread.start();//开始运行线程
Thread.sleep(5000);
synchronized (object){
object.notify();//main线程等待5秒后开始唤醒thread线程
}
}
}
运行结果如下:
2.3notifyAll方法
notify()和notifyAll()方法都用来唤醒wait()阻塞方法。
但是notify()方法一次只能某一个等待线程,而notifyAll()方法一次调用可以唤醒全部的相同锁对象的wait()方法。notifyAll()方法虽然能一次唤醒多个线程,但是多个线程要竞争锁,并不是同步执行,也是依次执行。
除此之外,notifyAll()方法与以上notify()方法基本相同。这里不在多说。
以上便是wait与notify方法的介绍与用法。