请用 Java 手写一个生产者消费者模型,要求使用 wait 和 notify 机制实现线程间的协作与同步。
考察说明
考查候选人是否理解 Java 内置的 wait、notify 机制以及生产者消费者模型中的线程同步与协作。
回答思路
- 【回答框架 1】生产者消费者模型解决的是生产者和消费者之间速度不匹配的问题,核心是共享缓冲区。在 Java 中,使用 wait 和 notify 实现时,需要定义一个共享对象作为锁,通常是一个缓冲区类,内部维护一个队列和容量上限。
- 【回答框架 2】生产者在放入数据前,需要检查缓冲区是否已满,如果已满则调用 wait 方法释放锁并进入等待状态;否则放入数据,并调用 notifyAll 唤醒可能等待的消费者。消费者在取出数据前,检查缓冲区是否为空,如果为空则 wait,否则取出数据并 notifyAll 唤醒生产者。
- 【回答框架 3】关键点在于 wait 和 notify 必须放在同步代码块或同步方法中,且 wait 会释放锁,而 notify 不会释放锁,只是唤醒一个等待线程。为了避免信号丢失,通常使用 while 循环检查条件,而不是 if,因为线程被唤醒后可能再次竞争锁,条件可能再次不满足。
- 【回答框架 4】实现时,可以定义一个 Buffer 类,包含 put 和 take 方法,内部使用 LinkedList 作为队列,使用 synchronized 关键字修饰方法,或者使用 synchronized 块。生产者和消费者线程分别循环调用 put 和 take。
- 【回答框架 5】需要注意,notify 唤醒的是任意一个等待线程,而 notifyAll 唤醒所有等待线程。在只有一个生产者和一个消费者时,notify 足够,但多个生产者消费者时,使用 notifyAll 更安全,避免死锁。
- 【关键点 1】wait 和 notify 必须在同步代码块或同步方法中使用,否则抛出 IllegalMonitorStateException。
- 【关键点 2】使用 while 循环检查条件,避免虚假唤醒和信号丢失。
- 【关键点 3】notifyAll 比 notify 更安全,适用于多生产者多消费者场景。
- 【关键点 4】缓冲区容量有限,生产者在满时等待,消费者在空时等待。
- 【关键点 5】wait 会释放锁,notify 不会释放锁,只是唤醒线程。
- 【易错点 1】使用 if 而不是 while 检查条件,可能导致线程在条件不满足时继续执行。
- 【易错点 2】在同步代码块外调用 wait 或 notify,导致运行时异常。
- 【易错点 3】使用 notify 而不是 notifyAll,在多消费者场景下可能造成死锁。