1
votes

I am new to programming and have only done Python in the past. I am working on a university assignment about implementing the Producer-Consumer problem in Java using multithreading concepts. In this case, there are two producers and one consumer. Producer A produces an item at the rate of one every 5 minutes (only an example), producer B produces an item at the rate of one every 6 minutes. The items are placed in a limited size shelf which fits only 5 items.

The consumer takes the item from the shelf every 4 minutes. The consumer will be unable to operate when the shelf is empty and the producers will be unable to operate when the shelf is full.

The program should continue until each producer has produced 10 items and the consumer has consumed 20 items.

This is what I have done so far:

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;
}
}

Above block of code is the class for the shelf mentioned.

The block of code below is the producer class. I have decided to use an array instead of LinkedList since we are not allowed to use SynchronizedList for this assignment.

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);

        }
    }
}
}

However, I've run into a problem. Since Java wouldn't allow me to append to an array, I have no idea how to continue the code. I initially planned to append the value of pot to:

int[] queue

to visualise the producer producing the item and putting it into the shelf.

I'd really appreciate your help!

2
Use something you can append to, like a List.Scott Hunter
is List a type of SynchronizedList? Sorry, I know almost nothing about Java and this is all new to meMilkTable

2 Answers

0
votes

If you want to minimize using library classes, you can easily make a ring buffer with a fixed size array. Keep track of two indexes: where the next item should be inserted, and where the next item can be removed https://en.wikipedia.org/wiki/Circular_buffer

Ring buffers are used often in high performance concurrent code so it makes sense to use them in the context of this exercise. Your "limited size shelf" pretty much sounds like a ring buffer.

0
votes

I would move the queue into the Buffer class. With the queue encapsulated into the Buffer, the producers and consumers do not have to synchronize. The Buffer can protect its contents from concurrent access and the threads accessing it don't have to care.

In real life you don't want correctness of something like this to depend on whether random threads accessing it are well-behaved. This will also be a major simplification that makes your code easier to reason about.

For the queue implementation, as long as the Buffer protects against concurrent access to the list, using a non-threadsafe list implementation is fine. Here that means all methods that allow accessing or modifying the list have to be protected with synchronized.

You need to add notifying on both adding and removing so that threads waiting on either condition get woken up.

A weirdness of using Java's intrinsic locks is the api was oversimplified to make it more accessible, but in a way that causes pitfalls. If threads wait on an object for different reasons, then using notify is problematic, because the scheduler picks what thread gets notified yet the scheduler has no idea which condition threads are waiting on, so if it picks a thread that is waiting on a condition that isn't ready yet, that notification goes to waste. A fix for that problem is to use notifyAll instead of notify. (A better fix is, don't use intrinsic locks, use ReentrantLock instead, because it can provide different conditions for the same lock.)