本节讲解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();
    }
} 

Java中wait和notifyAll/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版)》

 这里我们创建了一个生产者producer,2个消费者,consumer1、consumer2。其中队列对象blockingQueue 在主线程中创建,子线程是共享的。我们对goodsList使用了synchronized对象锁,不管是取出还是插入其它的线程都是不可操作的,所以从上面的输出结果中,2个消费者是分开消费的。