目录
1.什么是线程的等待通知
2.wiat()方法
2.1 wait 做的事情:
2.2wait 结束等待的条件:
代码示例:
2.3wait的三种重载方式
2.4 面试问题:wait()和sleep()之间的区别
3.notify()方法
3.1notify ?法是唤醒等待的线程.
3.2wait 和notify之间的联系
3.3notifyAll()
1.什么是线程的等待通知
线程的等待通知是多线程编程中常用的一种机制,用于线程之间的协作和同步。在Java中,线程的等待通知通过使用
由于线程之间是抢占式执?的, 因此线程之间执?的先后顺序难以预知. 但是实际开发中有时候我们希望合理的协调多个线程之间的执?先后顺序,所以我们需要wait()、notify()来实现这些功能。
2.wiat()方法
2.1 wait 做的事情:
?
使当前执?代码的线程进?等待. (把线程放到等待队列中)?
释放当前的锁?
满??定条件时被唤醒, 重新尝试获取这个锁.
注意:wait 要搭配 synchronized 来使?. 脱离 synchronized 使? wait 会直接抛出异常.
2.2wait 结束等待的条件:
?
其他线程调?该对象的 notify ?法.?
wait 等待时间超时 (wait ?法提供?个带有 timeout 参数的版本, 来指定等待时间).?
其他线程调?该等待线程的 interrupted ?法, 导致 wait 抛出
InterruptedException
异常.
代码示例:
package 多线程; public class ThreadDemo18 { public static void main(String[] args) throws InterruptedException { Object object = new Object();//new一个对象 synchronized (object){//对对象进行加锁 System.out.println("等待中···"); object.wait();//当前线程会释放对象的锁,并进入等待状态。 System.out.println("等待结束。"); } } }
主线程会打印"等待中···",然后调用
2.3wait的三种重载方式
第一种wait()重载方式:它是死等的,就是说如果没有notify()方法来唤醒它,它就一直处于阻塞状态。
第二种wait()重载方式:它自己设定了一个超时的时间,单位是ms。就是说它最多等待到设定的时间,在这个时间内没有notify也不等了,直接会被唤醒。
第三种wait()重载方式:方法使当前线程进入等待状态,直到其他线程调用该对象
2.4 面试问题:wait()和sleep()之间的区别
wait提供了一个带超时间的版本
sleep也是可以指定时间 都是时间到了解除阻塞。
wait和sleep都是可以提前被唤醒的
wait通过notify()来唤醒
sleep通过interrupt来唤醒
使用wait的主要目的,一定是不知道要等多少时间的前提下来使用的。所谓的超时间,其实是“兜底的”。
使用sleep,一定是知道要等多长时间,必须要等到那个时间才会被唤醒。
3.notify()方法
3.1notify ?法是唤醒等待的线程.
?
?法notify()也要在同步?法或同步块中调?,该?法是?来通知那些可能等待该对象的对象锁的其 它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。?
如果有多个线程等待,则有线程调度器随机挑选出?个呈 wait 状态的线程。(并没有 "先来后到")?
在notify()?法后,当前线程不会?上释放该对象锁,要等到执?notify()?法的线程将程序执? 完,也就是退出同步代码块之后才会释放对象锁。
代码示例:
package 多线程; public class ThreadDemo19 { public static void main(String[] args) { //需要一个统一的对象进行加锁,wait,notify Object locker = new Object(); Thread t1 = new Thread(()->{ synchronized (locker){ System.out.println("t1 wait 之前"); try { locker.wait(); } catch (InterruptedException e) { e.printStackTrace(); } 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) { e.printStackTrace(); } }); } }
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-motify 之后,释放锁,t2执行完毕.
所以输出结果如下:
3.2wait 和notify之间的联系
wait 和notify之间是通过Object对象联系起来的。
Object1.wait()
Object2.notify() 此时如果用notify是无法被唤醒的,必须是两个对象一样才能唤醒。
Object1.wait()
Object2.wait()此时notify使用的哪个对象,哪个对象才能被唤醒。
3.3notifyAll()
唤醒这个对象上所有等待的线程.假设有很多个线程,都使用同一个对象 wait.针对这个对象进行 notifyAII,此时就会全都唤醒~~但是注意,这些线程在wait返回的时候,要重新获取锁,就会因为锁的竞争,使这些线程实际上是一个一个串行执行的.
具体使用方式如下:
notifyAll() 方法必须在同步代码块或同步方法中调用,并且只能应用于被synchronized 关键字修饰的对象上。当调用对象的
notifyAll() 方法时,该对象上所有等待的线程都会被唤醒,并尝试重新获取对象的锁。被唤醒的线程会进入就绪状态,然后根据线程调度机制竞争获取锁。
只有获取到对象的锁的线程才能继续执行同步代码块中的内容,而其他未获取到锁的线程仍然处于阻塞状态,直到再次获得锁的机会。
希望大家多多支持!