目录
🚩运行结果
🚩运行结果
我们知道了,线程具有随机调度的特点,但是有时候我们确实需要控制线程的执行顺序。目前我们知道 join 可以控制执行顺序。但是通过 join 会直接使一个线程直接阻塞,并不是我们想要的效果。我们需要的是将线程的某段逻辑阻塞等待。所谓我们需要使用 wait 。使用wait需要和 notify搭配使用。
使用(观察执行顺序)
一个wait对应一个notify,并别都需要搭配锁(synchronized)使用。
wait()不带参数的api
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
Object clock = new Object();
Thread t1 = new Thread(()->{
while (true){
synchronized (clock){
try {
System.out.println("wait 开始");
clock.wait();
System.out.println("wait 结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t1.start();
Thread.sleep(1000);
Thread t2 = new Thread(()->{
synchronized (clock){
System.out.println("notify 开始");
clock.notify();
System.out.println("notify 结束");
}
});
t2.start();
}
}
通过结果的打印顺序,我们来看wait 和notify的相互搭配使用。
运行结果
完整的执行逻辑
可以看到,t1线程里有while循环,t2线程没有,我们让t1线程先执行了一秒,但是我们看到打印了wait开始只有并没有紧接着打印wait结束,原因就是我们调用了clock.wait,于是解锁,阻塞等待t2线程,当t2线程 打印了notify开始,执行了clock.notify,我们t1线程的wait阻塞才被解开,但是由于notify也占用着锁,t1 线程获取不到锁,不能接着往下执行打印wait结束语句,等t2线程执行了打印notify结束,释放了锁,t1线程才能重新拿到锁然后接着往下执行打印wait结束语句,此时t2线程没有循环,也就是线程执行结束了,但是t1线程的逻辑处在while死循环里,又打印了wait开始和clock.wait语句,但是等不到notify就出现了上面的打印结果。整个main线程也结束不了。
wait(time)带参数的api
参数表是当前wait的最大阻塞等待时间,如果超时了,线程就会继续往下执行。
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
Object clock = new Object();
Thread t1 = new Thread(()->{
synchronized (clock){
try {
System.out.println("wait 开始");
clock.wait(2000);
System.out.println("wait 结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
Thread.sleep(4000);
Thread t2 = new Thread(()->{
synchronized (clock){
System.out.println("notify 开始");
clock.notify();
System.out.println("notify 结束");
}
});
t2.start();
}
}
运行结果
我们设置了wait的最大等待时间为两秒,但是我们的notify在t2线程,而t2线程我们设置了sleep(4000),才能开始执行,明显超过了wait的最大等待时间,而从结果上来看,也确实跟我们的预期一样。
空打一炮问题
虽然我们的main线程也是正常退出了,但是实际上我们的notify的使用上出现了问题,wait和notify是互相搭配使用的,是wait阻塞了,收到了notify解锁的通知接着执行,但是上述代码中我们的notify实际上并没有解锁(wait的阻塞),因为在之前wait就执行结束了,这就造成了空打一炮的结果。虽然并没有对我们的程序造成什么影响,但是这样的使用是错误的。
wait和sleep的对比