Author:MTingle
major:人工智能---------------------------------------
Build your hopes like a tower!
目录
一、wait 和 notify是什么
wait(等待) notify(通知) 机制,和join用途类似,多个线程之间随机调度,引入wait notify就是为了能够从应用层面上,干预到多个不同线程代码的执行顺序(不会影响线程随机调度的策略),相当于在应用程序代码中,让后执行的线程主动放弃被调度的机会,就可以让先执行的线程先把对应的代码执行完.
二、wait 和 notify 操作
1.wait notify
1.join与wait的不同:join是等待另一个线程执行完才继续执行,wait是等待另一个线程通过notify进行通知,不要求另一个线程必须执行完
2.wait的作用:
1) 释放锁
2) 线程进入阻塞状态,通过 1 和 2 其他进程就有机会拿到锁了(进程进入阻塞,无法打印"wait之后")
public class ThreadDemo24 {
public static void main(String[] args) throws InterruptedException {
Object object=new Object();
synchronized (object) {
System.out.println("wait之前");
object.wait();
System.out.println("wait之后");
};
}
}
3) 当其他线程调用notify时,wait解除阻塞,并重新获取到锁
3.调用wait的对象必须和synchronized中的锁对象一致,因此wait解的锁必然是解的object的锁,后续被notify唤醒之后,重新获得的锁就是获取到object的锁(一个对象一把锁,拿到的是之前的锁)
4.如果有其他线程也尝试获取锁,wait被唤醒之后,也要重新参与到锁的竞争中
5.wait 和 sleep join 是一类,都可能会被interrupt提前唤醒
6.wait必须放到synchronized里头使用,因为释放锁的前提是拿到锁
public class ThreadDemo25 {
public static void main(String[] args) {
Object locker=new Object();
Thread t1=new Thread(()->{
synchronized (locker) {
System.out.println("t1 wait之前");
try {
locker.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("t1 wait之后");
}
});
Thread t2=new Thread(()->{
try {
Thread.sleep(5000);
synchronized (locker) {
System.out.println("t2 notify之前");
locker.notify();
System.out.println("t2 notify之后");
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
t1.start();
t2.start();
}
}
理解上述代码的执行过程:
1) t1 执行起来之后就会立即拿到锁,并且打印 t1 wait 之前,并进入wait方法(释放锁+阻塞等待)
2) t2执行起来之后,先进行sleep(5000)(这个sleep就可以让t1能够先拿到锁)
3) t2 sleep结束之后,由于 t1 是 wait 状态,锁是释放的,t2 就拿到锁,接下来打印 t2 notify之前 ,执行notify操作,这个操作就能够唤醒 t1 此时 t1 就从waiting 状态恢复回来了
4) 但是由于 t2 此时还没释放锁, t1 waiting恢复之后,再次尝试获取锁,就可能会出现阻塞,这个阻塞是由于锁竞争引起的
5) t2执行完 t2 notify之后,释放锁, t2 执行完毕, t1 的 wait 就可以获取到锁了,然后执行打印 t1 wait 之后
1) wait的两个版本:
第一个:死等(下策,没有回旋余地)
第二个:带有超时时间的等待,单位是ms,当进入wait的时候,最多等待 timeout ms ,如果这个时间内没有其他线程notify,也不等了(鲁棒性高----容错能力高)
2) wait 和 notify 是通过 object 对象联系起来的
object1.wait()
object2.notify()
此时无法唤醒,两个对象必须一致才能唤醒,notify使用的是哪个对象唤醒的就是哪个对象的wait
3) notifyAll: 唤醒这个对象上所有等待的线程
假设有很多个线程,都使用同一个对象 wait ,针对这个对象进行 notifyAll ,此时所有线程都会被唤醒,这些进程在wait返回时,谁先拿到,谁后拿到是不确定的.相比之下,更倾向于使用notify,notifyAll全部唤醒后不太好控制.
2.wait 和 sleep 的区别
1) wait 提供了一个带有超市时间的版本,sleep也是能指定时间的,都是时间到了就继续执行,解除阻塞
2) wait 和 sleep 都可以被提前唤醒(虽然时间没到,但是也能提前唤醒) wait 通过 notify 唤醒, sleep 通过 interrupt 唤醒
3) 使用 wait 最主要的目标,一定是不知道要等多少时间的前提下使用的,所谓的 timeout(超时时间)其实是兜底的,使用 sleep ,一定是直到要等多少时间的前提下使用的,虽然能提前唤醒,但是通过异常唤醒,这个操作不应该称为"正常的业务流程",异常唤醒通常是出现一些特殊情况了
4) 不建议完全使用 wait 来代替 sleep ,这种做法虽然可行,但是不被广泛认同,非常少见