一、 什么是线程通讯
由于线程之间是抢占式执⾏的, 因此线程之间执⾏的先后顺序难以预知.
但是实际开发中有时候我们希望合理的协调多个线程之间的执⾏先后顺序.
线程通讯中的注意事项
1.wait / notify / notifyAll 必须配合synchronized一起使用,一定要使用同一个对象进行加锁
2.当调用了notify/notifyAll之后程序并不会立即恢复执行,而是尝试获取锁资源,只有获取到锁之后才能继续执行。
3.notifyAll并不是唤醒所有的wait等待的线程,而是唤醒当前对象处于wait等待的所有线程。
5.没有配合synchronized使用会产生无锁,再无锁情况下,调用了wait和notify之后,线程的wait会永久处于等待状态。
二、线程通讯的方法介绍
完成这个协调⼯作(线程通讯),主要涉及到三个⽅法:
1.wait() / wait(long timeout):让当前线程进⼊等待状态。
2.notify():唤醒当前对象上⼀个休眠的线程(随机)。
3.notifyAll():唤醒当前对象上的所有线程。
注意这三个⽅法都需要配合 synchronized ⼀起使⽤。
synchronized(lock)中的lock即对象必须保持一致
notify和notifyAll调用之后并不是在里面立即唤醒,而是得等到notify线程执行完成之后才开始被正正的唤醒,进入就绪等待队列
三、wait的 执行流程:
1.使当前执⾏代码的线程进⾏等待. (把线程放到等待队列中)
2.释放当前的锁
3.满⾜⼀定条件时被唤醒, 重新尝试获取这个锁.
wait 要搭配 synchronized 来使⽤. 脱离 synchronized 使⽤ wait 会直接抛出异常
wait 结束等待的条件:
1.其他线程调⽤该对象的 notify ⽅法.
2.wait 等待时间超时 (wait ⽅法提供⼀个带有 timeout 参数的版本, 来指定等待时间).
3.其他线程调⽤该等待线程的 interrupted ⽅法, 导致 wait 抛出 InterruptedException 异常
Object lock = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock) {
System.out.println("开始执⾏线程1");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1执⾏完成");
}
});
t1.start();
四、notify和notifyAll的使用
notify ⽅法是唤醒等待的线程:
1.⽅法notify()也要在同步⽅法或同步块中调⽤,该⽅法是⽤来通知那些可能等待该对象的对象锁的其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。
2.如果有多个线程等待,则有线程调度器随机挑选出⼀个呈 wait 状态的线程。(并没有 “先来后到”)
3.在notify()⽅法后,当前线程不会⻢上释放该对象锁,要等到执⾏notify()⽅法的线程将程序执⾏
完,也就是退出同步代码块之后才会释放对象锁。
Object lock = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock) {
System.out.println("开始执⾏线程1");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1执⾏完成");
}
});
t1.start();
synchronized (lock) {
lock.notify();
notify⽅法只是唤醒某⼀个等待线程. 使⽤notifyAll⽅法可以⼀次唤醒所有的等待线程。
package Thread;
public class WaitDemo3 {
public static void main(String[] args) {
Object lock1 = new Object();
Object lock2 = new Object();
Thread t1 = new Thread(()->{
System.out.println("线程开始执行");
try {
synchronized(lock1) {
// 无限期的等待状态
lock1.wait();
System.out.println("线程1:恢复执行后进入休眠状态");
Thread.sleep(1000);
System.out.println("线程1执行完");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"线程1");
Thread t2 = new Thread(()->{
System.out.println("线程2开始执行d");
try {
synchronized(lock1){
lock1.wait();
System.out.println("线程2:恢复执行后进入休眠状态");
Thread.sleep(1000);
System.out.println("线程2执行完");
}
} catch (InterruptedException e) {
}
});
Thread t3 = new Thread(()->{
System.out.println("线程3开始执行");
try {
synchronized(lock1){
lock1.wait();
System.out.println("线程3:恢复执行后进入休眠状态");
Thread.sleep(1000);
System.out.println("线程3执行完");
}
} catch (InterruptedException e) {
}
});
t1.start();
t2.start();
t3.start();
Thread t4 = new Thread(()->{
System.out.println("线程4:开始执行");
synchronized(lock1){
lock1.notify();
lock1.notifyAll();
System.out.println("线程4:执行了唤醒操作");
System.out.println("线程4:synchronized 执行完了");
}
});
t4.start();
}
}