生产者消费者之Java简单实现

为什么要使用生产者和消费者模式

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

什么是生产者消费者模式

  • 生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
  • 这个阻塞队列就是用来给生产者和消费者解耦的。纵观大多数设计模式,都会找一个第三者出来进行解耦,如工厂模式的第三者是工厂类,模板模式的第三者是模板类。

代码实现(多生产者 和 多消费者)

  • QueueBuffer : 实现阻塞队列,将生产者和消费者解耦。它底层是一个数组,构造的时候指定数组的大小。由于实现的时多生产这和多消费者的模型,所以注意一下 put 和 get 中对阻塞条件的描述用的是while循环,这是为了生产者之间或者消费者之间他们的内部竞争所造成的数组越界异常。

clipboard.png

package concurrency;

public class QueueBuffer {
    private final int SIZE;
    private int count = 0;
    private int[] buffer;
    public QueueBuffer(int size){
        this.SIZE = size;
        buffer = new int[SIZE];
    }

    public int getSIZE(){
        return SIZE;
    }

    public synchronized void put(int value){
        while (count == SIZE){ //buffer已经满了 等待get   ,用while使用于多个生产者的情况
            try {
                wait();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }

        notifyAll(); //说明buffer中有元素 可以取
        buffer[count++] = value;
        System.out.println("Put "+value+" current size = "+count);
    }

    public synchronized int get(){
        while(count == 0){//用while使用于多个消费者的情况。
            try {
                wait();//buffer为空,需要等到put进元素
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
//        notify() 只是去通知其他的线程,但是synchronized 方法里面的代码还是会执行完毕的。
//        synchronized方法本来就加了锁。代码的执行跟你的notify()也无关,代码的执行是跟你的
//        synchronized绑定一起而已。

        notifyAll(); //说明刚刚从buffer中取出了元素 有空位可以加进新的元素
        int result = buffer[--count];
        System.out.println("Get "+result+" current size = "+count);
        return result;
    }
}

class Test{
    public static void main(String[] args){
        QueueBuffer q = new QueueBuffer(10);
        new Producer(q);
        new Producer(q);
        new Producer(q);
        new Consumer(q);
        new Consumer(q);
        new Consumer(q);
        System.out.println("Press Control-C to stop.");
    }
}

Producer

package concurrency;

import java.util.Random;

public class Producer implements Runnable {

    Random rand = new Random(47);

    private QueueBuffer q;

    Producer(QueueBuffer q) {
        this.q = q;
        new Thread(this, "Producer").start();
    }

    public void run() {
        while (true) {
            q.put(rand.nextInt(q.getSIZE()));
            Thread.yield();
        }
    }
}

Consumer

package concurrency;

public class Consumer implements Runnable {
    private QueueBuffer q;

    Consumer(QueueBuffer q) {
        this.q = q;
        new Thread(this, "Consumer").start();
    }

    public void run() {
        while (true){
            q.get();
            Thread.yield();
        }
    }
}

注意事项

  • 调用obj的wait(), notify()方法前,必须获得obj对象的锁,也就是必须写在synchronized(obj) {…} 代码段内。
  • 调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj) {…} 代码段内唤醒A。
  • 当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。
  • 如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。
  • obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。
  • 当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会获得锁继续执行。这一点很重要,并不是调用 notify 或者 notifyAll 之后马上释放锁,而是执行完相应的synchronized代码段。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
生产者消费者问题是一个经典的并发编程问题,可以用Java中的多线程机制来实现。 下面是一个简单Java实现示例: ```java import java.util.LinkedList; public class ProducerConsumer { public static void main(String[] args) { LinkedList<Integer> queue = new LinkedList<>(); int maxSize = 5; Thread producer = new Thread(new Producer(queue, maxSize), "Producer"); Thread consumer = new Thread(new Consumer(queue), "Consumer"); producer.start(); consumer.start(); } } class Producer implements Runnable { private final LinkedList<Integer> queue; private final int maxSize; public Producer(LinkedList<Integer> queue, int maxSize) { this.queue = queue; this.maxSize = maxSize; } @Override public void run() { while (true) { synchronized (queue) { while (queue.size() == maxSize) { try { System.out.println("Queue is full, Producer thread is waiting for consumer to consume items..."); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } int num = (int) (Math.random() * 100); queue.add(num); System.out.println("Produced item: " + num); queue.notifyAll(); } } } } class Consumer implements Runnable { private final LinkedList<Integer> queue; public Consumer(LinkedList<Integer> queue) { this.queue = queue; } @Override public void run() { while (true) { synchronized (queue) { while (queue.isEmpty()) { try { System.out.println("Queue is empty, Consumer thread is waiting for producer to produce items..."); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } int num = queue.removeFirst(); System.out.println("Consumed item: " + num); queue.notifyAll(); } } } } ``` 这个示例中,有一个生产者线程和一个消费者线程,它们共享一个队列。生产者线程会不断地往队列中添加随机数,如果队列已满,则等待消费者线程消费后再继续生产。消费者线程会不断地从队列中取出数字并输出,如果队列为空,则等待生产者线程生产后再继续消费。 需要注意的是,在访问共享资源(也就是队列)时要使用`synchronized`关键字进行同步,同时使用`wait()`和`notifyAll()`方法进行线程间通信,以保证线程安全和正确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值