Java多线程中的生产者消费者问题解决方案

Answered using GPT-4o-mini by Java Q&A Hub

Question:

Java面试题之多线程 如何写代码来解决生产者消费者问题?

Answer:

生产者消费者问题是一个经典的多线程问题,涉及到两个线程:生产者和消费者。生产者负责生成数据并将其放入缓冲区,而消费者则从缓冲区中取出数据进行处理。为了避免缓冲区溢出和空消费,需要使用适当的同步机制。

下面是一个使用Java的wait()notify()方法来解决生产者消费者问题的示例代码:

import java.util.LinkedList;
import java.util.Queue;

class ProducerConsumer {
    private final Queue<Integer> queue = new LinkedList<>();
    private final int LIMIT = 10; // 缓冲区的大小
    private final Object lock = new Object(); // 锁对象

    // 生产者
    public void produce() throws InterruptedException {
        int value = 0;
        while (true) {
            synchronized (lock) {
                // 如果缓冲区满,等待消费者消费
                while (queue.size() == LIMIT) {
                    lock.wait();
                }
                // 生产数据
                queue.add(value);
                System.out.println("Produced: " + value);
                value++;
                // 通知消费者可以消费
                lock.notifyAll();
            }
            Thread.sleep(1000); // 模拟生产时间
        }
    }

    // 消费者
    public void consume() throws InterruptedException {
        while (true) {
            synchronized (lock) {
                // 如果缓冲区空,等待生产者生产
                while (queue.isEmpty()) {
                    lock.wait();
                }
                // 消费数据
                int value = queue.poll();
                System.out.println("Consumed: " + value);
                // 通知生产者可以生产
                lock.notifyAll();
            }
            Thread.sleep(1000); // 模拟消费时间
        }
    }

    public static void main(String[] args) {
        ProducerConsumer pc = new ProducerConsumer();

        Thread producerThread = new Thread(() -> {
            try {
                pc.produce();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        Thread consumerThread = new Thread(() -> {
            try {
                pc.consume();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        producerThread.start();
        consumerThread.start();
    }
}

代码说明:

  1. 缓冲区:使用Queue<Integer>来作为缓冲区,限制其大小为LIMIT
  2. 锁对象:使用一个Object作为锁,确保对缓冲区的访问是线程安全的。
  3. 生产者方法
    • 在生产者方法中,使用synchronized块来获取锁。
    • 如果缓冲区已满,调用lock.wait()使生产者线程等待。
    • 生产数据并将其添加到缓冲区,然后调用lock.notifyAll()通知消费者线程。
  4. 消费者方法
    • 在消费者方法中,同样使用synchronized块来获取锁。
    • 如果缓冲区为空,调用lock.wait()使消费者线程等待。
    • 从缓冲区中取出数据并处理,然后调用lock.notifyAll()通知生产者线程。
  5. 主方法:创建并启动生产者和消费者线程。

注意事项:

  • 使用wait()notifyAll()时,必须在synchronized块中调用。
  • 生产者和消费者的执行速度可能不同,因此需要适当的等待和通知机制来协调它们的工作。