由于线程之间的执行是抢占式的,所以线程之间的执行的先后顺序难以预知。但在实际开发中有时我们需要各个线程以特定的顺序执行,例如生产者与消费者模型中
- 只有生产者线程生产出了产品消费者线程才能去取
- 只有消费者线程消费了产品(有冗余空间后)生产者线程才能生产
在Java多线程中,要完成这个协调工作会涉及如下方法
wait()/wait(long timeout)
:让当前线程进入等待状态notify()
:唤醒在当前对象上等待的线程notifyAll()
:同上
注意:wait
,notify
,notifyAll
都是Object
类的方法,这意味着可以使用任意类的实例能都调用这些方法
一:wait()方法
wait()方法:线程执行到wait
就会发生阻塞,直到另一个线程调用notify
将此wait
唤醒。wait
要搭配synchronized
使用,脱离synchronized
使用wait会直接抛出异常。执行到wait
会做以下三件事情
- 释放当前的锁
- 使线程进入等待
- 满足一定条件,结束等待,尝试获取锁
wait结束等待的条件有
- 其他线程调用该对象的
notify
方法 - wait等待时间超时(
wait(long timeout)
) - 其他线程调用该等待线程的
interrupted
方法,导致wait
抛出InterruptedException
如下
public class TestDemo {
public static void main(String[] args) {
Object object = new Object();
System.out.println("wait之前");
synchronized (object){
try {
object.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("wait之后");
}
}
二:notify()方法
notify()方法:用于唤醒等待的线程,需要在同步方法或同步块中调用。注意
- 如果有多个线程等待,则由线程调度器随机挑选出一个处于
wait
状态的线程 - 在
notify
方法之后,当前线程不会马上释放该对象锁,在退出同步代码块之后才会释放对象锁
如下是一个典型的例子
public class TestDemo {
public static void main(String[] args) {
//准备一个对象,需要保证wait和notify是同一个对象
Object object = new Object();
//第一个线程,进行wait操作
Thread t1 = new Thread("t1"){
@Override
public void run(){
while(true){
synchronized (object){
System.out.println(Thread.currentThread().getName() + "进入并占用锁,持续3s");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
System.out.println(Thread.currentThread().getName() + "等待并释放锁");
object.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("等待结束," + Thread.currentThread().getName() + "重新获取到锁");
}
}
}
};
t1.start();
//第二个线程,进行notify
Thread t2 = new Thread("t2"){
@Override
public void run(){
while(true){
synchronized (object){
System.out.println(Thread.currentThread().getName() + "进入并占用锁,持续3s");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "进行notify");
object.notify();
System.out.println("notify完成,让" + Thread.currentThread().getName() + "歇3s");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println(Thread.currentThread().getName() + "已处在synchronized外,再歇10s");
try {
Thread.sleep(12000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
};
t2.start();
}
}
下面是一个动图,展示了程序的运行过程
评论区