本节讲解wait()和notifyAll/notify方法的意义和注意项,然后使用一个生产者和消费者例子来理解这两个方法的用法。
wait()
wait()的作用是让当前运作中的线程进入等待状态,可以通过其它线程调用notify()或者notifyAll()方法重新进入就绪状态。
wait()方法可以传入timeout超时参数,如果在timeout时间过后仍旧没有线程将自己唤醒,则自己进入就绪状态。
notifyAll()/notify()
- notify()是随机唤醒某一个进入等待状态的线程。
- notifyAll()是唤醒所有进入等待状态的线程。
注意:只有已经获取锁的线程,才可以调用锁的wait()、notify()方法,否则会抛出异常IllegalMonitorStateException。
如下代码:
class ThreadTask extends Thread {
private String lock;
public ThreadTask(String lock) {
this.lock = lock;
}
@Override
public void run() {
while (true) {
synchronized (lock){
try {
System.out.println("等待");
lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("解锁");
}
}
}
}
public class HelloWorld {
public static void main(String[] args) {
String lock = "lock";
ThreadTask task = new ThreadTask(lock);
task.start();
lock.notify();
}
}
在上面的例子中,在主线程没有获取锁的情况下直接调用lock.notify(),会报 Exception in thread "main" java.lang.IllegalMonitorStateException 异常。
我们把上面的代码重新修改下即可:
String lock = "lock";
ThreadTask task = new ThreadTask(lock);
task.start();
Thread.sleep(1000);
synchronized (lock){
lock.notifyAll();
}
wait和notifyAll/notify例子
Java中wait和notifyAll/notify 生产者消费者的例子如下:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
class Utils{
public static String getDateTime(){
Date date = new Date();
SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd :hh:mm:ss");
return dateFormat.format(date);
}
//获取一个随机商品
public static String getGoods(){
String [] goodsArr = {"《代码整洁之道》","《程序员修炼之道:从小工到专家》","《重构——改善既有代码的设计》","《Java并发编程实战》","《微服务架构设计模式》","《Scala编程(第3版)》"};
Random r = new Random();
int i = r.nextInt(goodsArr.length);
return goodsArr[i];
}
}
/**
* 生产者
*/
class Producer extends Thread {
private BlockingQueue goodsList;
public Producer(BlockingQueue goodsList) {
this.goodsList = goodsList;
}
@Override
public void run() {
while (true) {
synchronized (goodsList){
if (goodsList.size() <10) {
String goods = Utils.getGoods();
System.out.println(Utils.getDateTime() + " " + this.getName() + ":正在生产," + goods);
goodsList.add(goods);//添加一个随机商品
goodsList.notifyAll();
} else {
try {
goodsList.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
/**
* 消费者
*/
class Consumer extends Thread{
private BlockingQueue goodsList;
public Consumer(BlockingQueue goodsList) {
this.goodsList = goodsList;
}
@Override
public void run() {
while (true) {
synchronized (goodsList){
if (goodsList.size() >0 ) {
// System.out.println(Utils.getDateTime() + " " + this.getName() + "正在消费");
String goods = (String) goodsList.poll();
System.out.println(Utils.getDateTime() + " " + this.getName() + "取出商品"+goods);//从队列中取出商品
goodsList.notifyAll();
} else {
try {
goodsList.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
public class HelloWorld {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>();
Producer producer = new Producer(blockingQueue);//生产者
Consumer consumer1 = new Consumer(blockingQueue);//消费者1
Consumer consumer2 = new Consumer(blockingQueue);//消费者2
producer.start();
consumer1.start();
consumer2.start();
}
}
输出结果:
2022-12-12 :11:02:33 Thread-0:正在生产,《代码整洁之道》
2022-12-12 :11:02:33 Thread-0:正在生产,《微服务架构设计模式》
2022-12-12 :11:02:33 Thread-0:正在生产,《微服务架构设计模式》
2022-12-12 :11:02:33 Thread-0:正在生产,《微服务架构设计模式》
2022-12-12 :11:02:33 Thread-0:正在生产,《重构——改善既有代码的设计》
2022-12-12 :11:02:33 Thread-0:正在生产,《代码整洁之道》
2022-12-12 :11:02:33 Thread-0:正在生产,《重构——改善既有代码的设计》
2022-12-12 :11:02:33 Thread-0:正在生产,《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-0:正在生产,《微服务架构设计模式》
2022-12-12 :11:02:33 Thread-0:正在生产,《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-2取出商品《代码整洁之道》
2022-12-12 :11:02:33 Thread-2取出商品《微服务架构设计模式》
2022-12-12 :11:02:33 Thread-2取出商品《微服务架构设计模式》
2022-12-12 :11:02:33 Thread-2取出商品《微服务架构设计模式》
2022-12-12 :11:02:33 Thread-2取出商品《重构——改善既有代码的设计》
2022-12-12 :11:02:33 Thread-2取出商品《代码整洁之道》
2022-12-12 :11:02:33 Thread-2取出商品《重构——改善既有代码的设计》
2022-12-12 :11:02:33 Thread-2取出商品《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-2取出商品《微服务架构设计模式》
2022-12-12 :11:02:33 Thread-2取出商品《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-0:正在生产,《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-0:正在生产,《代码整洁之道》
2022-12-12 :11:02:33 Thread-0:正在生产,《代码整洁之道》
2022-12-12 :11:02:33 Thread-0:正在生产,《程序员修炼之道:从小工到专家》
2022-12-12 :11:02:33 Thread-0:正在生产,《Java并发编程实战》
2022-12-12 :11:02:33 Thread-0:正在生产,《Java并发编程实战》
2022-12-12 :11:02:33 Thread-0:正在生产,《Java并发编程实战》
2022-12-12 :11:02:33 Thread-0:正在生产,《程序员修炼之道:从小工到专家》
2022-12-12 :11:02:33 Thread-0:正在生产,《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-0:正在生产,《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-1取出商品《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-1取出商品《代码整洁之道》
2022-12-12 :11:02:33 Thread-1取出商品《代码整洁之道》
2022-12-12 :11:02:33 Thread-1取出商品《程序员修炼之道:从小工到专家》
2022-12-12 :11:02:33 Thread-1取出商品《Java并发编程实战》
2022-12-12 :11:02:33 Thread-1取出商品《Java并发编程实战》
2022-12-12 :11:02:33 Thread-1取出商品《Java并发编程实战》
2022-12-12 :11:02:33 Thread-1取出商品《程序员修炼之道:从小工到专家》
2022-12-12 :11:02:33 Thread-1取出商品《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-1取出商品《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-0:正在生产,《微服务架构设计模式》
2022-12-12 :11:02:33 Thread-0:正在生产,《微服务架构设计模式》
2022-12-12 :11:02:33 Thread-0:正在生产,《微服务架构设计模式》
2022-12-12 :11:02:33 Thread-0:正在生产,《重构——改善既有代码的设计》
2022-12-12 :11:02:33 Thread-0:正在生产,《代码整洁之道》
2022-12-12 :11:02:33 Thread-0:正在生产,《重构——改善既有代码的设计》
2022-12-12 :11:02:33 Thread-0:正在生产,《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-0:正在生产,《微服务架构设计模式》
2022-12-12 :11:02:33 Thread-0:正在生产,《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-2取出商品《代码整洁之道》
2022-12-12 :11:02:33 Thread-2取出商品《微服务架构设计模式》
2022-12-12 :11:02:33 Thread-2取出商品《微服务架构设计模式》
2022-12-12 :11:02:33 Thread-2取出商品《微服务架构设计模式》
2022-12-12 :11:02:33 Thread-2取出商品《重构——改善既有代码的设计》
2022-12-12 :11:02:33 Thread-2取出商品《代码整洁之道》
2022-12-12 :11:02:33 Thread-2取出商品《重构——改善既有代码的设计》
2022-12-12 :11:02:33 Thread-2取出商品《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-2取出商品《微服务架构设计模式》
2022-12-12 :11:02:33 Thread-2取出商品《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-0:正在生产,《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-0:正在生产,《代码整洁之道》
2022-12-12 :11:02:33 Thread-0:正在生产,《代码整洁之道》
2022-12-12 :11:02:33 Thread-0:正在生产,《程序员修炼之道:从小工到专家》
2022-12-12 :11:02:33 Thread-0:正在生产,《Java并发编程实战》
2022-12-12 :11:02:33 Thread-0:正在生产,《Java并发编程实战》
2022-12-12 :11:02:33 Thread-0:正在生产,《Java并发编程实战》
2022-12-12 :11:02:33 Thread-0:正在生产,《程序员修炼之道:从小工到专家》
2022-12-12 :11:02:33 Thread-0:正在生产,《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-0:正在生产,《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-1取出商品《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-1取出商品《代码整洁之道》
2022-12-12 :11:02:33 Thread-1取出商品《代码整洁之道》
2022-12-12 :11:02:33 Thread-1取出商品《程序员修炼之道:从小工到专家》
2022-12-12 :11:02:33 Thread-1取出商品《Java并发编程实战》
2022-12-12 :11:02:33 Thread-1取出商品《Java并发编程实战》
2022-12-12 :11:02:33 Thread-1取出商品《Java并发编程实战》
2022-12-12 :11:02:33 Thread-1取出商品《程序员修炼之道:从小工到专家》
2022-12-12 :11:02:33 Thread-1取出商品《Scala编程(第3版)》
2022-12-12 :11:02:33 Thread-1取出商品《Scala编程(第3版)》
这里我们创建了一个生产者producer,2个消费者,consumer1、consumer2。其中队列对象blockingQueue 在主线程中创建,子线程是共享的。我们对goodsList使用了synchronized对象锁,不管是取出还是插入其它的线程都是不可操作的,所以从上面的输出结果中,2个消费者是分开消费的。