由于线程之间是抢占式执?的, 因此线程之间执?的先后顺序难以预知. 但是实际开发中有时候我们希望合理的协调多个线程之间的执?先后顺序.
1. 方法介绍
完成这个协调?作(
线程通讯
),主要涉及到三个?法:
???????
?● wait() / wait(long timeout):让当前线程进?等待状态。
????????● notify():唤醒当前对象上?个休眠的线程(随机)。
????????● notifyAll():唤醒当前对象上的所有线程。
注意这三个?法都需要配合 synchronized ?起使?。
这三个方法Object的内置方法,对象级别。
?
2. wait使用
?
wait 执?流程:
??●?使当前执?代码的线程进?等待. (把线程放到等待队列中)
??●?释放当前的锁(释放的是自己的锁)
??●?满??定条件时被唤醒, 重新尝试获取这个锁
wait 要搭配 synchronized 来使?. 脱离 synchronized 使? wait 会直接抛出异常.
?
wait 结束等待的条件:
??●?其他线程调?该对象的 notify ?法.
??●?wait 等待时间超时 (wait ?法提供?个带有 timeout 参数的版本, 来指定等待时间).
??●?其他线程调?该等待线程的 interrupted ?法, 导致 wait 抛出 InterruptedException 异常.
wait使用:
public class WaitDemo {
public static void main(String[] args) {
Object lock = new Object();
Thread t1 = new Thread(() -> {
System.out.println("线程1开始执行");
try {
synchronized (lock) {
System.out.println("线程1调用wait方法....");
// 无限期的等待状态
lock.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1执行完成");
}, "线程1");
t1.start();
}
}
输出:
线程1开始执行
线程1调用wait方法....
(程序仍在执行)
?
3. notify使用
notify ?法是唤醒等待的线程:
??●??法notify()也要在同步?法或同步块中调?,该?法是?来通知那些可能等待该对象的对象锁的其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。
??●?如果有多个线程等待,则有线程调度器随机挑选出?个呈 wait 状态的线程。(并没有 "先来后到")
??●?在notify()?法后,当前线程不会?上释放该对象锁,要等到执?notify()?法的线程将程序执?
完,也就是退出同步代码块之后才会释放对象锁。
public class WaitDemo2 {
public static void main(String[] args) {
Object lock = new Object();
Object lock2 = new Object();
Thread t1 = new Thread(() -> {
System.out.println("线程1开始执行");
try {
synchronized (lock) {
System.out.println("线程1调用wait方法....");
// 无限期的等待状态
lock.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1执行完成");
}, "线程1");
Thread t2 = new Thread(() -> {
System.out.println("线程2开始执行");
try {
synchronized (lock) {
System.out.println("线程2调用wait方法....");
// 无限期的等待状态
lock.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程2执行完成");
}, "线程2");
t2.start();
t1.start();
// 唤醒 lock 对象上休眠的线程的(随机唤醒一个)
Thread t4 = new Thread(() -> {
//先让线程1,2,3先执行
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
}
System.out.println("线程4:开始执行");
synchronized (lock) {
// 发出唤醒通知
lock.notify();
System.out.println("线程4:执行了唤醒操作");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println("线程4:synchronized 执行完了");
}
}, "线程4");
t4.start();
}
}
输出:
线程1开始执行
线程2开始执行
线程1调用wait方法....
线程2调用wait方法....
线程4:开始执行
线程4:执行了唤醒操作
线程4:synchronized 执行完了
线程1执行完成
?
?
4. notifyAll使用
notify?法只是唤醒某?个等待线程. 使?notifyAll?法可以?次唤醒所有的等待线程。
public class WaitDemo3 {
public static void main(String[] args) {
Object lock = new Object();
Object lock2 = new Object();
Thread t1 = new Thread(() -> {
System.out.println("线程1开始执行");
try {
synchronized (lock) {
System.out.println("线程1调用wait方法....");
// 无限期的等待状态
lock.wait();
System.out.println("线程1:恢复执行之后又进入休眠状态");
Thread.sleep(2000);
System.out.println("线程1执行完成");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程1");
Thread t2 = new Thread(() -> {
System.out.println("线程2开始执行");
try {
synchronized (lock) {
System.out.println("线程2调用wait方法....");
// 无限期的等待状态
lock.wait();
System.out.println("线程2:恢复执行之后又进入休眠状态");
Thread.sleep(2000);
System.out.println("线程2执行完成");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程2");
Thread t3 = new Thread(() -> {
System.out.println("线程3开始执行");
try {
synchronized (lock) {
System.out.println("线程3调用wait方法....");
// 无限期的等待状态
lock.wait();
System.out.println("线程3:恢复执行之后又进入休眠状态");
Thread.sleep(2000);
System.out.println("线程3执行完成");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程3");
t2.start();
t1.start();
t3.start();
// 唤醒 lock 对象上休眠的线程的(随机唤醒一个)
Thread t4 = new Thread(() -> {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
}
System.out.println("线程4:开始执行");
synchronized (lock) {
// 发出唤醒通知
lock.notifyAll();
lock.notifyAll();
System.out.println("线程4:执行了唤醒操作");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println("线程4:synchronized 执行完了");
}
}, "线程4");
t4.start();
}
}
输出:
线程2开始执行
线程2调用wait方法....
线程3开始执行
线程1开始执行
线程3调用wait方法....
线程1调用wait方法....
线程4:开始执行
线程4:执行了唤醒操作
线程4:synchronized 执行完了
线程1:恢复执行之后又进入休眠状态
线程1执行完成
线程3:恢复执行之后又进入休眠状态
线程3执行完成
线程2:恢复执行之后又进入休眠状态
线程2执行完成
修改线程3中的lock为lock2
try {
synchronized (lock2) {
System.out.println("线程3调用wait方法....");
// 无限期的等待状态
lock2.wait();
System.out.println("线程3:恢复执行之后又进入休眠状态");
Thread.sleep(2000);
System.out.println("线程3执行完成");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
输出:
线程2开始执行
线程1开始执行
线程3开始执行
线程2调用wait方法....
线程3调用wait方法....
线程1调用wait方法....
线程4:开始执行
线程4:执行了唤醒操作
线程4:synchronized 执行完了
线程1:恢复执行之后又进入休眠状态
线程1执行完成
线程2:恢复执行之后又进入休眠状态
线程2执行完成
?由上述两个代码可以得出,notifyAll 并不是唤醒所有的wait 等待的线程,而是唤醒当前对象处于wait 等待的所有线程。
5. 注意事项
????????1. wait / notify / notifyAll 必须要配合synchronized一起执行。
????????2. wait / notify / notifyAll 进行?synchronized 加锁,一定要使用同一个·对象进行加锁,不然会报错。
????????3. 当调用了notify /??notifyAll 之后,程序并不会立即恢复执行,而是尝试获取锁,只有得到锁之后才能继续执行。
? ? ? ? 4. notifyAll 并不是唤醒所有的wait 等待的线程,而是唤醒当前对象处于wait 等待的所有线程。
|