Проблема производителя-потребителя без использования потоково-безопасных классов, таких как AtomicInteger и SynchronizedList - PullRequest
1 голос
/ 26 марта 2020

Я новичок в программировании и сделал только 1021 * в прошлом. Я работаю над университетским заданием по реализации проблемы Продюсер-Потребитель в Java с использованием концепции многопоточности. В этом случае есть два производителя и один потребитель. Производитель A производит предмет из расчета один раз в 5 минут (только пример), производитель B производит предмет из расчета один раз в 6 минут. Предметы размещаются на полке ограниченного размера, которая вмещает только 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 не позволяет мне добавлять массив, я понятия не имею, как продолжить код. Первоначально я планировал добавить значение банка к:

int[] queue

, чтобы визуализировать производителя, производящего предмет и кладущего его на полку.

Буду очень признателен за вашу помощь!

Ответы [ 2 ]

0 голосов
/ 26 марта 2020

Я бы переместил очередь в класс Buffer. Поскольку очередь помещена в буфер, производители и потребители не должны синхронизироваться. Буфер может защитить свое содержимое от одновременного доступа, и потоки, обращающиеся к нему, не должны заботиться.

В реальной жизни вы не хотите, чтобы правильность чего-либо подобного зависела от того, хорошо ли ведут себя случайные потоки, обращающиеся к нему. Это также будет основным упрощением, облегчающим рассуждение вашего кода.

Для реализации очереди, если буфер защищает от одновременного доступа к списку, использование реализации без поточного списка - это нормально. Здесь это означает, что все методы, которые позволяют получить доступ к списку или изменить его, должны быть защищены синхронизированными.

Необходимо добавить уведомление как при добавлении, так и при удалении, чтобы разбудить потоки, ожидающие при любом условии.

Странность использования внутренних замков Java c заключается в том, что API был упрощен, чтобы сделать его более доступным, но таким образом, что это приводит к подводным камням. Если потоки ожидают объекта по разным причинам, тогда использование notify проблематично c, поскольку планировщик выбирает, какой поток получает уведомление, но планировщик не знает, какие потоки условия ожидают, поэтому, если он выбирает поток, который ожидает условие, которое еще не готово, это уведомление теряется. Решение этой проблемы - использовать notifyAll вместо notify. (Лучшее решение - не использовать блокировки intrinsi c, вместо этого используйте ReentrantLock, поскольку он может обеспечить различные условия для одной и той же блокировки.)

0 голосов
/ 26 марта 2020

Если вы хотите минимизировать использование библиотечных классов, вы можете легко создать кольцевой буфер с массивом фиксированного размера. Следите за двумя индексами: где следующий элемент должен быть вставлен, и где следующий элемент может быть удален https://en.wikipedia.org/wiki/Circular_buffer

Кольцевые буферы часто используются в высокопроизводительном параллельном коде, поэтому смысл использовать их в контексте этого упражнения. Ваша «полка ограниченного размера» в значительной степени звучит как кольцевой буфер.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...