生产者消费者设计模式

生产者消费者设计模式学习

一、什么是生产者消费者设计模式

Java中的生产者-消费者设计模式是一种用于多线程编程的经典设计模式,它用于解决多个线程之间共享资源时的同步和通信问题。这个模式主要用在有数据生产者(Producer)和数据消费者(Consumer)的场景中,生产者负责产生数据,而消费者负责消费数据。

二、为什么会有这种模式

在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。
在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。
为了解决这种生产消费能力不均衡的问题,所以便有了生产者和消费者模式。

三、是用来解决什么问题的

  • 资源共享:在多线程环境中安全地共享资源。
  • 数据同步:确保生产者不会在没有消费者的情况下过度填充缓冲区,反之亦然。
  • 异步通信:允许生产者和消费者独立工作,但又能在适当的时候交互。

四、如何解决的

上个小demo

package com.ljh.producerconsumer;

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

public class producerConsumerDemo {
    public static void main(String[] args) {
        Buffer buffer=new Buffer();
        Consumer consumer=new Consumer(buffer);
        Producer producer=new Producer(buffer);
        
        producer.start();
        consumer.start();


    }
}

/**
 * 生产者类,继承自Thread,负责向缓冲区添加数据。
 * 该类的实例将作为一个线程运行,不断地向缓冲区添加数据。
 */
class Producer extends Thread {
    /**
     * 缓冲区对象,用于存储生产者产生的数据。
     */
    private Buffer buffer;

    /**
     * 构造函数,初始化生产者的缓冲区。
     * 
     * @param buffer 缓冲区实例,生产者将向这个缓冲区添加数据。
     */
    public Producer(Buffer buffer) {
        this.buffer = buffer;
    }

    /**
     * 重写run方法,定义生产者的线程执行逻辑。
     * 生产者会循环10次,每次向缓冲区添加一个数字,并休眠1秒。
     */
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                // 向缓冲区添加数据。
                buffer.add(i);
                // 休眠1秒,模拟生产数据的过程。
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // 如果线程被中断,抛出运行时异常。
                throw new RuntimeException(e);
            }
        }
    }
}

/**
 * 消费者类,继承自Thread,负责从缓冲区获取数据并消费。
 */
class Consumer extends Thread {
    /**
     * 缓冲区对象,用于存储和提取数据。
     */
    private Buffer buffer;

    /**
     * 构造函数,初始化消费者类的缓冲区对象。
     * 
     * @param buffer 缓冲区对象,用于数据消费。
     */
    public Consumer(Buffer buffer) {
        this.buffer = buffer;
    }

    /**
     * 重写run方法,定义消费者线程的执行逻辑。
     * 主要负责从缓冲区获取数据并打印,重复执行10次。
     */
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            int val = 0;
            try {
                // 从缓冲区获取数据,如果被中断则抛出RuntimeException。
                val = buffer.pull();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            // 打印消费了的数据。
            System.out.println("消费了" + val);
        }
    }
}


/**
 * Buffer类用于实现一个固定大小的缓冲区。
 * 它使用队列来存储数据,并且支持线程安全的添加和提取数据操作。
 */
class Buffer {
    /**
     * 使用LinkedList作为队列实现,因为其在添加和删除操作上具有较好的性能。
     */
    private Queue<Integer> queue = new LinkedList<>();
    
    /**
     * 缓冲区的大小被固定为5,这个大小决定了可以存储的最大数据量。
     */
    private int size = 5;
    
    /**
     * 向缓冲区中添加一个数据项。
     * 如果缓冲区已满,则当前线程进入等待状态,直到缓冲区有空位。
     * 
     * @param val 要添加到缓冲区的值。
     * @throws InterruptedException 如果线程在等待时被中断。
     */
    public synchronized void add(int val) throws InterruptedException {
        // 如果队列已满,则等待直到有空间可用。
        if (queue.size() > size) {
            wait();
        }
        // 添加新值到队列。
        queue.add(val);
        // 唤醒其他等待的线程,可能有线程在等待消费数据。
        notify();
    }
    
    /**
     * 从缓冲区中提取一个数据项。
     * 如果缓冲区为空,则当前线程进入等待状态,直到有数据可用。
     * 
     * @return 从缓冲区中提取的数据项。
     * @throws InterruptedException 如果线程在等待时被中断。
     */
    public synchronized int pull() throws InterruptedException {
        // 如果队列为空,则等待直到有数据可用。
        if (queue.size() == 0) {
            wait();
        }
        // 提取并返回队列的第一个元素。
        int val = queue.poll();
        // 唤醒其他等待的线程,可能有线程在等待添加数据。
        notify();
        return val;
    }
}

posted @ 2024-07-22 21:03  jhhhred  阅读(207)  评论(0)    收藏  举报