wait和notify
因为多线程的调度是随机的,很多时候希望多个线程能够按照咱们规定的顺序来执行,完成线程之间的配合工作,wait和notify就是一个用来协调线程顺序的重要工具.
这两个方法都是Object提供的方法.随便找个对象,都可以使用wait和notify
这个异常说明,当wait引起线程阻塞之后,可以使用 interrupt 方法,把线程给唤醒,可以打断当前的阻塞状态的.
我们运行程序过后就会发现,这里还是有报错
wait的执行逻辑
wait在执行的时候,会做三件事
1.解锁:Object.wait就会尝试针对Object对象解锁.
2.阻塞等待
3.当被其他线程唤醒之后(一般wait搭配notify进行唤醒),就会尝试重新加锁,加锁成功,wait执行完毕,继续往下执行其他逻辑.
wait要解锁,前提就是先能加上锁,锁都没加,就想解锁,这里就当然会报错了.
如何解决?
核心思路:先加锁,在synchronized里面再进行wait!
此时它的状态是waiting
这里的wait就会一直阻塞到其他线程进行 notify 了.
wait和notify主要的用途就是用来安排线程之间的执行顺序,其中一个最典型的场景,就是能有效避免"线程饿死/饥饿(也就是吃不到CPU的资源)"
假设现在有ABCD四个线程,A线程现在在占用锁,当A线程释放锁之后,此时所有线程一拥而上,都来尝试获取这个锁,此时一种情况是,ABCD四个线程一拥而上,都去尝试获取这把锁,此时第二次获取到锁的不是A线程,另一种情况就是,A线程刚刚将锁释放掉,但是A线程同样会参与到锁竞争当中,此时A线程近水楼台先得月,又将锁占据了,后一种情况就会导致BCD线程发生"线程饿死"这种情况
public class Demo15 {
private static Object object=new Object();
public static void main(String[] args) {
Thread t1=new Thread(()->{
while(true){
synchronized (object){
while(true){
System.out.println("wait开始");
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("wait结束");
}
}
}
});
t1.start();
Thread t2=new Thread(()->{
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object){
System.out.println("notify开始");
object.notify();
System.out.println("notify结束");
}
}
});
t2.start();
}
}
几个注意事项:
1.要想让 notify 能够顺利唤醒 wait 就需要确保 wait 和 notify 都是使用同一个对象调用的.
2.wait 和 notify 都需要放到 synchronized 之内的.虽然 notify 不涉及"解锁操作"但是Java也强制要求notify要放到synchronized之中(系统的原生API中没有这个要求)
3.如果进行notify的时候,另一个线程并没有处于wait状态.此时notify相当于"空打一炮"不会有任何的副作用.
在此处对于我们的t1,t2线程来说,t2线程就是用来辅助t1线程,使用notify对线程进行一个统筹规划的作用.在实际开发中,有的时候需要有多个线程按照一定的顺序来完成某些工作,此时就可以安排一个线程统一负责规划程序(但是这样的代码写起来是比较复杂的,实际开发过程中往往也不会真的这么去写,可以使用其他的代码结构来代替这种写法.)
但是线程可能有多个
比如,可以有N个线程进行wait,一个线程负责notify.notify操作只会唤醒一个wait,具体是唤醒了哪个线程?是随机的!
notifyAll
notifyAll负责唤醒全部处于 waiting 中的线程(但是,我们不推荐使用这个,因为这个违背了wait的初心,我们使用wait是为了让线程具有一定的顺序,而这里如果使用notifyAll,就会使所有上锁的线程一起解锁,从而恢复混乱局面).
那么如果我们想唤醒某个指定的线程,那该怎么办呢?
可以让不同的线程,使用不同的对象来进行wait.想要唤醒谁,就可以使用对应的对象来notify.(也就是多搞几把锁)
wait和sleep之间的区别
sleep是有一个明确的时间的,到达时间,自然就会被唤醒.也能提前唤醒,使用interrupt就可以.
wait默认是一个死等,一直等到有其他线程的notify(这个是顺理成章的唤醒,唤醒之后该线程还需要继续工作,后续还会进入wait状态),wait也能够被interrupt提前唤醒(告知线程要结束了,接下来线程就要进入到收尾工作了).其实wait也有一个带有超时时间的版本的.(和join类似)
因此,协调多个线程之间的执行顺序,当然还是优先使用 wait notify 而不是 sleep.