多线程篇五——wait和notify
如笔者理解有误,欢迎交流指正⭐
线程的执行先后顺序难以预料【抢占式执行】,但是实际开发中我们会需要掌握当下线程的执行顺序.
这就是wait和notify的作用.【都是Object方法即随便定义一个对象豆可以使用wait和notify】
wait()方法
wait执行过程
1.释放当前的锁
2.让线程进入阻塞
3.当线程被唤醒的时候重新获取到锁
上代码
public class Demo15 {public static void main(String[] args) throws InterruptedException {Object object = new Object();synchronized (object) {System.out.println("waiting...");object.wait();System.out.println("end.");}}
}
运行发现wait一直处于等待状态
tips
wait搭配synchronized使用,synchronized加锁给对象头进行标记
wait结束等待的条件
其他线程调用该对象的notify方法
wait等待超时(timeout用来指定等待时间)
其他线程调用该等待线程的interrupted方法,抛出InterruptedException异常
wait和sleep的对比
共同点
让线程在一段时间里不执行.【wait用于线程间通信(生产者-消费者模型) sleep用于线程阻塞(延迟操作)】
不同点
1.wait()可通过notify()唤醒,sleep()通过Interrupt()唤醒
2.使用wait()时没有设置最大等待时间,而sleep()是在知道最大时限的情况下使用(通过异常唤醒,说明程序应该是出现特殊情况了).
3.wait()搭配synchronized使用,sleep()不用.
4.wait()是Object的方法,sleep()是Thread的静态方法.
5.wait()方法会释放锁,sleep()不会.
notify()方法
此时我们的notify()方法就担起了唤醒wait()方法的大任(唤醒等待的线程).
tips
notify()要在同步方法或同步块中调用.它会通知可能等待该对象对象锁的其他线程,使得它们重新获取对象锁.
如果有多个线程等待,有线程调度器随机挑选一个wait状态的线程[【无先来后到】
调用notify()方法后不会立即释放对象锁,要等到执行notify()方法的线程将程序执行完之后【退出同步代码块】才会释放对象锁.
notify唤醒线程过程
1.创建 WaitTask 类, 对应一个线程, run 内部循环调用 wait.
2.创建 NotifyTask 类, 对应另一个线程, 在 run 内部调用一次 notify 注意, WaitTask 和 NotifyTask 内部持有同一个 Object locker.
3.WaitTask 和 NotifyTask 要想配合 就需要搭配同一个 Object.
public class Demo16 {public static Object locker = new Object();public static void main(String[] args) {Thread t1 = new Thread(() -> {synchronized (locker) {System.out.println("t1 wait 之前");try {//t1执行到这,就会先立即释放锁,进入wait方法(释放锁+阻塞等待)locker.wait();System.out.println("t1 wait 之后");} catch (InterruptedException e) {e.printStackTrace();}}});Thread t2 = new Thread(() -> {try {//t2执行起来之后,先进行sleep(3000)(这个sleep操作就可以让t1先拿到锁)//如果先notify虽然不会有副作用(不会出现异常之类的),但是wait就无法被唤醒,逻辑上有问题Thread.sleep(3000);//t2sleep结束之后,由于t1是wait状态,t2就能拿到锁//接下来打印t2notify之前,执行notify操作,这个操作就能唤醒t1(此时t1就从WAITING状态恢复过来了)synchronized (locker) {System.out.println("t2 notify 之前");locker.notify();//但是由于t2此时还没有释放锁,WAITING恢复之后,尝试获取锁,就可能出现一个小小的阻塞,这个阻塞是由锁竞争引起的//t1目前处于BLOCKED状态,但是时间比较短,肉眼看不见System.out.println("t2 notify 之后");}//t2释放锁之后,就可以继续执行t1} catch (InterruptedException e) {e.printStackTrace();}});t1.start();t2.start();}}
notifyAll()方法
顾名思义哈 notifyAll()可以唤醒所有方法>
注意
唤醒的这些方法在等待(wait)返回时重新获取锁产生锁竞争,但实际这些锁是串行执行的【锁被哪个线程先获取是不确定的】
区别notify()和notifyAll()
1.唤醒一个/都唤醒
2.没有锁竞争/产生锁竞争
对比可发现notify()比notifyAll()好控制 发生错误的概率更低
未完待续❀❀❀