Java版生产者消费者模型,线程间通信
1.这是一个使用 Java 实现的生产者-消费者模型的示例,展示线程间通信。示例1使用BlockingQueue 来简化实现,BlockingQueue底层使用 wait() 和 notify() 机制来协调生产者和消费者线程。
示例1:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class ProducerConsumerExample {
    public static void main(String[] args) {
        // 创建一个容量为10的阻塞队列
        BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(10);
        // 创建生产者线程
        Thread producerThread = new Thread(new Producer(queue), "Producer");
        // 创建消费者线程
        Thread consumerThread = new Thread(new Consumer(queue), "Consumer");
        // 启动线程
        producerThread.start();
        consumerThread.start();
    }
}
class Producer implements Runnable {
    private final BlockingQueue<Integer> queue;
    public Producer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }
    @Override
    public void run() {
        try {
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + " producing: " + i);
                queue.put(i); // 放入队列,自动阻塞如果队列满
                Thread.sleep(1000); // 模拟生产耗时
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
class Consumer implements Runnable {
    private final BlockingQueue<Integer> queue;
    public Consumer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }
    @Override
    public void run() {
        try {
            while (true) {
                Integer item = queue.take(); // 从队列取出,自动阻塞如果队列空
                System.out.println(Thread.currentThread().getName() + " consuming: " + item);
                Thread.sleep(1500); // 模拟消费耗时
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
说明:
- BlockingQueue:使用 
LinkedBlockingQueue作为线程安全的队列,容量为10。put()和take()方法会自动处理线程同步,当队列满时生产者阻塞,当队列空时消费者阻塞。 - 生产者:
Producer线程循环生产1到10的整数,放入队列,每次生产后休眠1秒模拟耗时。 - 消费者:
Consumer线程从队列中取出数据并消费,每次消费后休眠1.5秒模拟耗时。 - 线程通信:
BlockingQueue内部使用wait()和notify()机制实现线程间通信,生产者和消费者通过队列协调工作。 - 异常处理:捕获 
InterruptedException并恢复中断状态,确保线程安全退出。 
运行结果:
运行程序后,您会看到生产者和消费者交替工作,输出类似:
Producer producing: 1
Consumer consuming: 1
Producer producing: 2
Consumer consuming: 2
...
这个示例展示了生产者-消费者模式的核心思想,适合初学者理解线程间通信。
2. 示例2是使用 wait() 和 notify() 手动实现的生产者-消费者模型,不依赖 BlockingQueue,而是使用自定义的缓冲区和同步机制来实现线程间通信。
示例2:
public class ProducerConsumerManual {
    public static void main(String[] args) {
        Buffer buffer = new Buffer(5); // 缓冲区容量为5
        // 创建生产者线程
        Thread producerThread = new Thread(new Producer(buffer), "Producer");
        // 创建消费者线程
        Thread consumerThread = new Thread(new Consumer(buffer), "Consumer");
        // 启动线程
        producerThread.start();
        consumerThread.start();
    }
}
class Buffer {
    private int[] items;
    private int size;
    private int count;
    private int in; // 生产者放入位置
    private int out; // 消费者取出位置
    public Buffer(int size) {
        this.items = new int[size];
        this.size = size;
        this.count = 0;
        this.in = 0;
        this.out = 0;
    }
    public synchronized void put(int item) throws InterruptedException {
        // 缓冲区满时等待
        while (count == size) {
            System.out.println(Thread.currentThread().getName() + " waiting: buffer full");
            wait();
        }
        // 放入数据
        items[in] = item;
        in = (in + 1) % size;
        count++;
        System.out.println(Thread.currentThread().getName() + " produced: " + item);
        // 通知消费者
        notify();
    }
    public synchronized int take() throws InterruptedException {
        // 缓冲区空时等待
        while (count == 0) {
            System.out.println(Thread.currentThread().getName() + " waiting: buffer empty");
            wait();
        }
        // 取出数据
        int item = items[out];
        out = (out + 1) % size;
        count--;
        System.out.println(Thread.currentThread().getName() + " consumed: " + item);
        // 通知生产者
        notify();
        return item;
    }
}
class Producer implements Runnable {
    private final Buffer buffer;
    public Producer(Buffer buffer) {
        this.buffer = buffer;
    }
    @Override
    public void run() {
        try {
            for (int i = 1; i <= 10; i++) {
                buffer.put(i);
                Thread.sleep(1000); // 模拟生产耗时
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
class Consumer implements Runnable {
    private final Buffer buffer;
    public Consumer(Buffer buffer) {
        this.buffer = buffer;
    }
    @Override
    public void run() {
        try {
            for (int i = 1; i <= 10; i++) {
                buffer.take();
                Thread.sleep(1500); // 模拟消费耗时
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
说明:
- 
Buffer 类:
- 使用固定大小的数组(容量为5)作为缓冲区。
 put()和take()方法使用synchronized确保线程安全。- 当缓冲区满(
count == size)时,生产者调用wait()等待;当缓冲区空(count == 0)时,消费者调用wait()等待。 - 生产或消费后,通过 
notify()唤醒等待的线程。 - 使用循环队列(
in和out指针)管理缓冲区位置。 
 - 
生产者:
- 生产1到10的整数,放入缓冲区,每次生产后休眠1秒模拟耗时。
 
 - 
消费者:
- 从缓冲区取出数据并消费,每次消费后休眠1.5秒模拟耗时。
 
 - 
线程通信:
- 使用 
wait()和notify()实现线程间通信,生产者和消费者通过缓冲区的状态(满或空)协调工作。 synchronized确保同一时间只有一个线程访问缓冲区。
 - 使用 
 - 
异常处理:
- 捕获 
InterruptedException并恢复中断状态,确保线程安全退出。 
 - 捕获 
 
运行结果:
运行程序后,您会看到生产者和消费者交替工作,输出类似:
Producer produced: 1
Consumer consumed: 1
Producer produced: 2
Consumer consumed: 2
Producer produced: 3
...
Producer waiting: buffer full
Consumer consumed: 8
Producer produced: 9
...
关键点:
- 使用 
while循环检查条件(而不是if),防止虚假唤醒(spurious wakeup)。 notify()只会唤醒一个等待线程,适合单生产者单消费者场景。如果需要支持多生产者多消费者,可以使用notifyAll()。- 相比 
BlockingQueue,手动实现更清晰地展示了wait()和notify()的工作原理。 
3. 示例3 是使用 wait() 和 notifyAll() 手动实现的多生产者多消费者模式的 Java 示例。这个示例支持多个生产者和消费者线程,通过同步机制协调访问共享缓冲区。使用 notifyAll() 代替 notify(),以确保在多线程场景下所有等待的线程都能被唤醒检查条件。
实例3:
public class MultiProducerConsumer {
    public static void main(String[] args) {
        Buffer buffer = new Buffer(5); // 缓冲区容量为5
        // 创建3个生产者线程
        for (int i = 1; i <= 3; i++) {
            new Thread(new Producer(buffer, i), "Producer-" + i).start();
        }
        // 创建2个消费者线程
        for (int i = 1; i <= 2; i++) {
            new Thread(new Consumer(buffer, i), "Consumer-" + i).start();
        }
    }
}
class Buffer {
    private int[] items;
    private int size;
    private int count;
    private int in; // 生产者放入位置
    private int out; // 消费者取出位置
    public Buffer(int size) {
        this.items = new int[size];
        this.size = size;
        this.count = 0;
        this.in = 0;
        this.out = 0;
    }
    public synchronized void put(int item, String producerName) throws InterruptedException {
        // 缓冲区满时等待
        while (count == size) {
            System.out.println(producerName + " waiting: buffer full");
            wait();
        }
        // 放入数据
        items[in] = item;
        in = (in + 1) % size;
        count++;
        System.out.println(producerName + " produced: " + item);
        // 通知所有等待线程
        notifyAll();
    }
    public synchronized int take(String consumerName) throws InterruptedException {
        // 缓冲区空时等待
        while (count == 0) {
            System.out.println(consumerName + " waiting: buffer empty");
            wait();
        }
        // 取出数据
        int item = items[out];
        out = (out + 1) % size;
        count--;
        System.out.println(consumerName + " consumed: " + item);
        // 通知所有等待线程
        notifyAll();
        return item;
    }
}
class Producer implements Runnable {
    private final Buffer buffer;
    private final int id;
    public Producer(Buffer buffer, int id) {
        this.buffer = buffer;
        this.id = id;
    }
    @Override
    public void run() {
        try {
            for (int i = 1; i <= 5; i++) { // 每个生产者生产5个数据
                int item = i * 100 + id; // 生产唯一数据,如101, 102, ..., 501, 502
                buffer.put(item, Thread.currentThread().getName());
                Thread.sleep((long) (Math.random() * 1000)); // 随机休眠0-1秒
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
class Consumer implements Runnable {
    private final Buffer buffer;
    private final int id;
    public Consumer(Buffer buffer, int id) {
        this.buffer = buffer;
        this.id = id;
    }
    @Override
    public void run() {
        try {
            for (int i = 1; i <= 8; i++) { // 每个消费者消费8个数据
                buffer.take(Thread.currentThread().getName());
                Thread.sleep((long) (Math.random() * 1500)); // 随机休眠0-1.5秒
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
说明:
- 
Buffer 类:
- 使用固定大小的数组(容量为5)作为循环缓冲区。
 put()和take()方法使用synchronized确保线程安全。- 当缓冲区满(
count == size)时,生产者调用wait()等待;当缓冲区空(count == 0)时,消费者调用wait()等待。 - 使用 
notifyAll()唤醒所有等待线程,适合多生产者多消费者场景,因为notify()可能只唤醒同类型线程(如只唤醒另一个生产者),导致死锁。 - 使用 
while循环检查条件,防止虚假唤醒。 
 - 
生产者:
- 创建3个生产者线程,每个生产5个数据(共15个数据)。
 - 每个生产者生成唯一的数据(如 Producer-1 生产 101, 201, ..., 501),便于区分。
 - 生产后随机休眠0-1秒,模拟不同生产速度。
 
 - 
消费者:
- 创建2个消费者线程,每个消费8个数据(共16个数据,略多于生产数据以确保缓冲区最终被清空)。
 - 消费后随机休眠0-1.5秒,模拟不同消费速度。
 
 - 
线程通信:
- 使用 
wait()和notifyAll()实现线程间通信。notifyAll()确保所有等待的生产者和消费者线程都能被唤醒,检查缓冲区状态。 - 同步方法确保同一时间只有一个线程访问缓冲区。
 
 - 使用 
 - 
异常处理:
- 捕获 
InterruptedException并恢复中断状态,确保线程安全退出。 
 - 捕获 
 
运行结果:
运行程序后,您会看到多个生产者和消费者交替工作,输出类似:
Producer-1 produced: 101
Producer-2 produced: 102
Consumer-1 consumed: 101
Producer-3 produced: 103
Consumer-2 consumed: 102
Producer-1 produced: 201
Producer-2 waiting: buffer full
Consumer-1 consumed: 103
Producer-2 produced: 202
...
关键点:
- notifyAll() vs notify():在多线程场景下,
notifyAll()确保所有等待线程(生产者和消费者)都被唤醒,避免只唤醒同类型线程导致的死锁。 - 唯一数据:生产者生成的数据包含线程ID(如101, 102),便于跟踪生产和消费的对应关系。
 - 随机休眠:模拟生产者和消费者处理速度的差异,增加并发场景的真实性。
 - 缓冲区管理:使用循环队列(
in和out指针)高效管理缓冲区空间。 
注意事项:
- 程序设计为生产15个数据,消费16个数据,确保所有数据被消费后,消费者可能因缓冲区空而等待。如果需要程序自动终止,可以在 
Buffer类中添加终止条件(如特殊值或标志)。 - 注意这里还可以进一步扩展,添加日志、性能监控,支持动态生产者/消费者数量 等等。
 
                    
                
                
            
        
浙公网安备 33010602011771号