目录
? ? ? ? 线程的等待通知是多线程编程中常用的一种机制,用于线程之间的协作和同步。在Java中,线程的等待通知通过使用wait()
、notify()
和notifyAll()
方法来实现。
? ? ? ? ?由于线程之间是抢占式执?的, 因此线程之间执?的先后顺序难以预知. 但是实际开发中有时候我们希望合理的协调多个线程之间的执?先后顺序,所以我们需要wait()、notify()来实现这些功能。
? 使当前执?代码的线程进?等待. (把线程放到等待队列中)? 释放当前的锁? 满??定条件时被唤醒, 重新尝试获取这个锁.
? 其他线程调?该对象的 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("等待结束。");
}
}
}
? ? ? ?主线程会打印"等待中···",然后调用object.wait()
方法,释放对象的锁,并进入等待状态。当其他线程调用了object.notify()
或object.notifyAll()
方法时,主线程会被唤醒,然后重新获取对象的锁,并继续执行输出"等待结束。"。但是这个代码种没有notify()方法,所以没有重新唤起线程,就没有输出等待结束。
?
? ? ? ? 第一种wait()重载方式:它是死等的,就是说如果没有notify()方法来唤醒它,它就一直处于阻塞状态。
? ? ? ? 第二种wait()重载方式:它自己设定了一个超时的时间,单位是ms。就是说它最多等待到设定的时间,在这个时间内没有notify也不等了,直接会被唤醒。
? ? ? ?第三种wait()重载方式:方法使当前线程进入等待状态,直到其他线程调用该对象notify()
、notifyAll()
方法唤醒它,或者指定的超时时间到达。这个方法允许设置纳秒级别的超时时间,除了毫秒级别的参数外,还可以指定纳秒级别的增量。
? ?wait提供了一个带超时间的版本? ?
? ? sleep也是可以指定时间? ? ? ? 都是时间到了解除阻塞。
wait和sleep都是可以提前被唤醒的
wait通过notify()来唤醒
sleep通过interrupt来唤醒
使用wait的主要目的,一定是不知道要等多少时间的前提下来使用的。所谓的超时间,其实是“兜底的”。
使用sleep,一定是知道要等多长时间,必须要等到那个时间才会被唤醒。?
? ?法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执行完毕.
所以输出结果如下:
wait 和notify之间是通过Object对象联系起来的。
Object1.wait()
Object2.notify() 此时如果用notify是无法被唤醒的,必须是两个对象一样才能唤醒。
Object1.wait()
Object2.wait()此时notify使用的哪个对象,哪个对象才能被唤醒。
? ? ? ? ?唤醒这个对象上所有等待的线程.假设有很多个线程,都使用同一个对象?wait.针对这个对象进行?notifyAII,此时就会全都唤醒~~但是注意,这些线程在wait返回的时候,要重新获取锁,就会因为锁的竞争,使这些线程实际上是一个一个串行执行的.
具体使用方式如下:
notifyAll()
方法必须在同步代码块或同步方法中调用,并且只能应用于被synchronized
关键字修饰的对象上。当调用对象的
notifyAll()
方法时,该对象上所有等待的线程都会被唤醒,并尝试重新获取对象的锁。被唤醒的线程会进入就绪状态,然后根据线程调度机制竞争获取锁。
只有获取到对象的锁的线程才能继续执行同步代码块中的内容,而其他未获取到锁的线程仍然处于阻塞状态,直到再次获得锁的机会。
希望大家多多支持!?