生产者-消费者问题,不使用诸如AtomicInteger和SynchronizedList之类的线程安全类

问题描述 投票:0回答:2

我是编程新手,过去只做过Python。我正在处理一个大学作业,涉及使用多线程概念在Java中实现生产者-消费者问题。在这种情况下,有两个生产者和一个消费者。生产者A以每5分钟1个的速率生成一个项目(仅作为示例),生产者B以每6分钟1个的速率生成一个项目。这些物品放在有限的架子上,只能容纳5个物品。

消费者每隔4分钟从货架上取走一件物品。当架子空时,消费者将无法操作,而当架子已满时,生产者将无法操作。

该程序应继续进行,直到每个生产者生产了10件商品,并且消费者消费了20件商品。

这是我到目前为止所做的:

class Buffer{

private int v;
private volatile boolean empty = true;

public synchronized void insert(int x) {
    while(!empty) {
        try {
            wait();
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }
    }
    empty = false;
    v = x;
    notify();
}

public synchronized int remove() {
    while(empty) {
        try {
            wait();
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }
    }
    empty = true;
    return v;
}
}

上面的代码块是提到的架子的类。

下面的代码块是生产者类。我决定使用数组而不是LinkedList,因为不允许我们为此任务使用SynchronizedList。

class Producer extends Thread{

private int size;
private int[] queue;
private int queueSize;

public Producer(int[] queueln, int queueSizeln, String ThreadName) {

    super(ThreadName);
    this.queue = queueln;
    this.queueSize = queueSizeln;
}

public void run() {

    while(true) {
        synchronized(queue) {
            while(size == queueSize) {
                System.out.println(Thread.currentThread().getName() + "Shelf is full: waiting...\n");
                try {
                    queue.wait();
                }
                catch(InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //when queue is empty, produce one, add and notify
            int pot = 1;
            System.out.println(Thread.currentThread().getName() + " producing...: " + pot);

        }
    }
}
}

但是,我遇到了一个问题。由于Java不允许我追加到数组,因此我不知道如何继续执行代码。我最初计划将pot的值附加到:

int[] queue

可视化生产者生产该物品并将其放入货架。

非常感谢您的帮助!

java multithreading producer-consumer
2个回答
0
投票

如果要最大程度地减少使用库类,则可以轻松制作具有固定大小的数组的环形缓冲区。跟踪两个索引:下一个项目应插入的位置,下一个项目可以去除的位置https://en.wikipedia.org/wiki/Circular_buffer

环形缓冲区通常在高性能并发代码中使用,因此在本练习的上下文中使用它们是有意义的。您的“有限尺寸的架子”听起来像是环形缓冲区。


0
投票

我会将队列移到Buffer类中。通过将队列封装到Buffer中,生产者和使用者不必同步,Buffer可以保护其内容免受并发访问,而访问它的线程也不必关心。

对于队列实现,只要Buffer防止对列表的并发访问,使用非线程安全列表实现就可以了。在这里,这意味着所有允许访问或修改列表的方法都必须使用同步保护。

您需要在添加和删除时都添加通知,以便唤醒在任一条件下等待的线程。

使用Java的内在锁的怪异之处在于,如果线程由于不同的原因在对象上等待,那么使用notify是有问题的,因为调度程序会选择要通知的线程,并且如果线程正在等待的条件不是准备好了,该通知将浪费掉。解决该问题的方法是使用notifyAll而不是notify。

© www.soinside.com 2019 - 2024. All rights reserved.