Hi I have been trying to solve the producer consumer problem in java without semaphores. When I use single producer and single consumer then my code is working fine. But when I add more than one consumer then it is completely messing up, all the consumer threads are going into the synchronized block. I'm not sure why this is happening. Here is my code :
Producer class:
public class Producer implements Runnable {
Object SharedObject = null;
String producerName= null;
Random rn = new Random();
public Producer(Main m, String s) {
this.SharedObject = m;
this.producerName=s;
}
public Producer(Main m) {
this.SharedObject = m;
}
public void run() {
while (true) {
synchronized (SharedObject) {
if (Main.itemCount == Main.bufferSize) {
try {
System.out.println("Producer is sleeping and waiting for notification form Consumer");
SharedObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Main.itemCount++;
System.out.println(this.producerName+" Produced the item and the item count is : " + Main.itemCount);
if (Main.itemCount == 1) {
SharedObject.notify();
System.out.println("Producer Notified the cosumer to wake up");
}
}
try {
int i = rn.nextInt(100);
Thread.sleep(i);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Consumer Class:
public class Consumer implements Runnable {
Object SharedObject = null;
String consumerName= null;
Random rn = new Random();
public Consumer(Main m, String s) {
SharedObject = m;
this.consumerName=s;
}
Consumer c= new Consumer((Main) SharedObject,consumerName);
synchronized void consume(){
synchronized (SharedObject) {
if (Main.itemCount == 0) {
try {
System.out.println(this.consumerName+" is sleeping and waiting for notify from Producer");
SharedObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Main.itemCount--;
System.out.println(this.consumerName+" consumed 1 item and the item Count is " + Main.itemCount);
if (Main.itemCount == 4) {
SharedObject.notifyAll();
System.out.println("Consumer notified the producer to wake up");
}
}
}
public void run() {
while (true) {
c.consume();
try {
int i = rn.nextInt(100);
Thread.sleep(i);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Main Class:
public class Main {
static int itemCount = 0;
static int bufferSize = 5;
public static void main(String[] args) {
Main m = new Main();
Thread objP = new Thread(new Producer(m, "Producer1"));
Thread objC = new Thread(new Consumer(m, "Consumer1"));
Thread objC2 = new Thread(new Consumer(m, "Consumer2"));
Thread objC3 = new Thread(new Consumer(m, "Consumer3"));
objP.start();
objC.start();
objC2.start();
objC3.start();
}
}
You are using notifyAll in the producer, which wakes up all consumer threads waiting on the monitor. If you want only one consumer to wake up, you should use notify From the API documentation:
notify()
Wakes up a single thread that is waiting on this object's monitor.
notifyAll()
Wakes up all threads that are waiting on this object's monitor.
It would also be better for your consumers to actually check that they can consume a resource when they are woken up. If you want to continue to use notifyAll, a consumer should be able to be awoken, and if insufficient resource is available, go back to waiting.
I suggest printing the main.itemCount. This will make it more obvious what the problems you have are.
You have to pay attention to when you are calling notify.
Why does your producer only call notify when there is exactly one item available? Shouldn't the producer call notify whenever there is an item available?
The consumer only tells the producer to wake up when there are 4 items (isn't this full?).
Actually changing notifyAll() to notify() kindoff worked!!! thanks for ua suggestion guys. Here is my code:
Producer class:
package com.source;
import java.util.Random;
public class Producer implements Runnable {
Object SharedObject = null;
String producerName = null;
Random rn = new Random();
public Producer(Main m, String s) {
this.SharedObject = m;
this.producerName = s;
}
public Producer(Main m) {
this.SharedObject = m;
}
public void run() {
while (true) {
synchronized (SharedObject) {
if (Main.itemCount == Main.bufferSize) {
try {
System.out
.println(this.producerName + "is sleeping and waiting for notification form Consumer");
SharedObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Main.itemCount++;
System.out.println(this.producerName + " Produced the item and the item count is : " + Main.itemCount);
if (Main.itemCount == 1) {
SharedObject.notify();
System.out.println("Producer Notified the cosumer to wake up");
}
}
try {
int i = rn.nextInt(100);
Thread.sleep(i);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Consumer Class:
package com.source;
import java.util.Random;
public class Consumer implements Runnable {
Object SharedObject = null;
String consumerName = null;
Random rn = new Random();
public Consumer(Main m, String s) {
SharedObject = m;
this.consumerName = s;
}
public void run() {
while (true) {
synchronized (SharedObject) {
if (Main.itemCount == 0) {
try {
System.out.println(this.consumerName + " is sleeping and waiting for notify from Producer");
SharedObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Main.itemCount--;
System.out.println(this.consumerName + " consumed 1 item and the item Count is " + Main.itemCount);
if (Main.itemCount == 4) {
SharedObject.notify();
System.out.println("Consumer notified the producer to wake up");
}
}
try {
int i = rn.nextInt(1000);
Thread.sleep(i);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Main Class:
package com.source;
public class Main {
static int itemCount = 0;
static int bufferSize = 5;
public static void main(String[] args) {
Main m = new Main();
Thread objP = new Thread(new Producer(m, "Producer1"));
Thread objC = new Thread(new Consumer(m, "Consumer1"));
Thread objC2 = new Thread(new Consumer(m, "Consumer2"));
Thread objC3 = new Thread(new Consumer(m, "Consumer3"));
Thread objP2 = new Thread(new Producer(m, "Producer2"));
Thread objP3 = new Thread(new Producer(m, "Producer3"));
objP.start();
objC.start();
objC2.start();
objC3.start();
objP2.start();
objP3.start();
}
}
Once again thanks to everyone for your valuable time and suggestions.
Sounds like you are past your initial problem but here's some more feedback.
I believe your real problem was not because of notifyAll() but because your buffer tests were if tests instead of while loops. There are classic race conditions where a thread gets awaken but there are no elements in the buffer. See my notes here. So you code should be something like:
while (Main.itemCount == Main.bufferSize) {
and
while (Main.itemCount == 0) {
Calling notifyAll() exacerbated the problem but the race conditions still exist even with just notify(). As you add more consumers or another producer you will see more problems.
Here is some other feedback.
Be very careful of locks within locks. That is a bad pattern typically and one that I use very infrequently. Do you really need consume() to be synchronized?
Object instance names should start with a lowercase letter so it should be sharedObject.
Any object that you are locking on should be private final if at all possible. You wouldn't want it changing to another object.
Using Main. anything is a bad pattern. How about creating an object with the itemCount and bufferSize and then passing the same instance of that object to all of our producer and consumers? It would also be the object you would lock on.
Be careful of sprinkling your thread code with System.out.println(...) messages as others have recommended. System.out is a synchronized class so this will add locks and memory synchronization that may move or fix the problem. Yes. Debugging threaded programs is hard.
Related
public class SemaphoreWithQueues implements Semaphore {
private List<Object> queue;
private AtomicInteger current = new AtomicInteger(0);
private int permits;
public SemaphoreWithQueues(int permits) {
this.permits = permits;
this.queue = Collections.synchronizedList(new LinkedList<>());
}
#Override
public void enter() throws InterruptedException {
if (current.get() < permits) {
current.incrementAndGet();
} else {
Object block = new Object();
synchronized (block) {
queue.add(block);
block.wait();
current.incrementAndGet();
}
}
}
#Override
public void leave() {
if(queue.size() != 0) {
Object block = queue.get(0);
queue.remove(0);
synchronized (block) {
block.notify(); //Unblock quenue
}
}
current.decrementAndGet();
//current lessen and current thread have time come in block if(...)
// in enter() faster then another thread increased current
}
}
> The program usually output:
>
> 1 1 2 2 1 1 2 2 1 2
**Where run() of both threads is almost the same, such as:**
public void run(){
for (int i = 0; i <5; i++) {
try {
semaphore.enter();
} catch (InterruptedException e) {
System.err.println(e);
}
System.out.println(2);
semaphore.leave();
}
}
There are 2 threads using this semaphore. When 1 thread increases the queue, the second is waiting, the problem is that if we extracted the object from quene and unblocked it, then the thread that finished leave() start enter() faster and again increments the counter, while the awaked thread also increments the counter, current = 2, and the list is empty.
SORRY FOR BAD ENGLISH
There are many problems in the code.
Synchronization: Synchronization should be done for a shareable
resource. Why it is done for a local object which has scope only for
that method.
Object block = new Object();
synchronized (block) {
Both current and queue are independent properties, they should be
synchronized together.
Now let's come to point If you really want to create a semaphore using Queue. You do not need all this logic. You can use existing Java class e.g. BlockingQueue. Here is the implementation
class SemaphoreWithQueues implements Semaphore{
private BlockingQueue<Integer> queue;
public SemaphoreWithQueues(int permits) {
if(queue == null){
queue = new ArrayBlockingQueue<>(permits);
}
}
public void enter() {
queue.offer(1);
System.out.println(Thread.currentThread().getName() + " got a permit.");
}
public void leave() throws InterruptedException {
queue.take();
System.out.println(Thread.currentThread().getName() + " left the permit.");
}
}
And Task to use the semaphore
class Task implements Runnable {
private SemaphoreWithQueues semaphore;
public Task(SemaphoreWithQueues semaphore){
this.semaphore = semaphore;
}
public void run(){
for (int i = 0; i <5; i++) {
semaphore.enter();
try {
semaphore.leave();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
SemaphoreWithQueues semaphoreWithQueues = new SemaphoreWithQueues(5);
Thread th1 = new Thread(new Task(semaphoreWithQueues));
Thread th2 = new Thread(new Task(semaphoreWithQueues));
Thread th3 = new Thread(new Task(semaphoreWithQueues));
th1.start();
th2.start();
th3.start();
}
}
But personally I do not like using Queue to create Semaphores, as it wastes memory unnecessary by creating elements in queue. Despite of this you can create a semaphore using single shareable object with permits using wait and notify mechanism. You can try with this approach. If you would like.
I am trying the Java thread producer and consumer program.
but consumer thread always goes to waiting status .
I unable to debug the issues why consumer thread always goes to waiting status or producer not notify to consumer thread
please help me to fix this . The programs are below.
The communicator class calls the both producer and consumer class
public class Communicator {
Thread t = null;
Thread t1 = null;
public void runThread() {
Producer p = new Producer();
Consumer c = new Consumer(p);
t = new Thread(p);
t1 = new Thread(c);
t.start();
t1.start();
Thread tr = new Thread() {
public void run() {
for (int i = 0; i < 30; i++) {
System.out.println("t::::::::::::: " + t.getState());
System.out.println("t1::::::::::::: " + t1.getState());
try {
Thread.sleep(2000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
};
tr.start();
}
public static void main(String[] args) {
Communicator c = new Communicator();
c.runThread();
}
}
This is producer class which append the data in stringbuffer and notify to consumer class
public class Producer extends Thread {
public StringBuffer sb;
public Producer() {
sb = new StringBuffer();
}
public void run() {
synchronized (sb) {
try {
System.out.println("Bala");
sb.append("murugan");
sb.notify();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Below is consumer class code . it wait up to get notifications from producer class.
public class Consumer extends Thread {
public Producer p;
public Consumer(Producer p) {
this.p = p;
}
public void run(){
synchronized (p.sb) {
try {
p.sb.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(p.sb);
}
}
}
There are few problems with your current code in which the Consumer thread always be in waiting state whereas the producer is already terminated.
Also, your StringBuffer object needs to be volatile so that the producer thread writes will be flushed & available to the other thread.
Along with that, I have modified your Producer and Consumer code to make it more realistic (both run in while loop one producing some data and the other receiving the data) as shown below: (I have also added 1 sec sleep to run the things in slower pace so that you can understand the things better):
Consumer class:
public class Producer extends Thread {
public volatile StringBuffer sb;
public Producer() {
sb = new StringBuffer();
sb.append("");
}
public void run() {
synchronized (sb) {
try {
while(true) {
Thread.sleep(1000);
if(sb.toString().equals("")) {
sb.append("murugan");
System.out.println(" producing sb completed *** ");
sb.notify();
} else {
sb.wait();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Consumer class:
public class Consumer extends Thread {
public Producer p;
public Consumer(Producer p) {
this.p = p;
}
public void run(){
synchronized (p.sb) {
try {
while(true) {
Thread.sleep(1000);
if(p.sb.toString().equals("")) {
p.sb.wait();
} else {
String str = p.sb.toString();
System.out.println(" consuming sb completed **** "+str);
p.sb.replace(0, str.length(), "");
p.sb.notify();
}
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(p.sb);
}
}
}
for your question,"I unable to debug the issues why consumer thread always goes to waiting status or producer not notify to consumer thread".
Actually your consumer is not always in wait status.
You can just put Thread.sleep(1000);before p.sb.wait(); in your Consumer class,then you can see "consumerThread::::::::::::: RUNNABLE" for once.
IMHO,your consumer code runs too fast to get wait status,so you miss the runnable status. You can learn more from other answers.
The Producer is already Terminated, and it already invoked notify() before the Consumer invoke wait().
Since Producer and Consumer extends Thread, update the Communicator class to this:
public class Communicator {
public void runThread() {
final Producer p = new Producer();
final Consumer c = new Consumer(p);
p.start();
c.start();
Thread tr = new Thread() {
public void run() {
for (int i = 0; i < 30; i++) {
System.out.println("t::::::::::::: " + p.getState());
System.out.println("t1::::::::::::: " + c.getState());
try {
Thread.sleep(2000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
};
tr.start();
}
public static void main(String[] args) {
Communicator c = new Communicator();
c.runThread();
}
}
If the Producer is not yet Terminated [if (p.getState() != Thread.State.TERMINATED)], that's the only time Consumer will wait:
public class Consumer extends Thread {
public Producer p;
public Consumer(Producer p) {
this.p = p;
}
public void run() {
synchronized (p.sb) {
try {
if (p.getState() != Thread.State.TERMINATED) {
p.sb.wait();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(p.sb);
}
}
}
It´s is not a answer but a suggestion ... You could simplify the whole logic using BlockingQueue to transfer data from Producer(s) to Consumer(s). All waits and notifies would disappear!
Producer(s) send data to be consumed calling BlockingQueue.offer(String)
Consumer(s) wait (blocked) for data calling BlockingQueue.pool();
As per your code, Consumer Thread waits for the Producer to notify about the string appended in the StringBuffer.
If Producer thread gets the chance to acquire the lock on shared StringBuffer object (it enters the synchronized block) then Consumer Thread will go in Blocked state(will not be able to enter the synchronized block) as its also a competitor for the Lock (both compete for acquiring the lock on same shared object).
Producer thread completes its execution, leaves the synchronized block and gets Terminated. Note that the notify code will not have any impact as Consumer thread is not yet waiting on the shared object as it is yet to enter the synchronized block
Consumer thread gets the chance to acquire the lock and enter the synchronized block It waits for some one to give notification on the shared object. But as Producer is already terminated no one gives the notification to Consumer thread and it remains in Waiting state.
Fix : In your case you can simply ensure that Consumer thread is started first and acquires the lock before Producer thread. For this you can have the main thread to sleep for some time after starting the Consumer thread.
t = new Thread(p);
t1 = new Thread(c);
t1.start();
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
t.start();
Key Point : In case you have only 2 threads, one thread should invoke notify and wait. Other thread upon being notified and only the thread in competition for Lock will acquire the lock and do its job. Upon completion of its job it should invoke notify and will wait for the other thread to do job and give notification once done. This way both the threads will get chance to do their jobs one after the other.
First of all, this is not a homework.
I have written a piece of code so that:
Thread-1 prints 1,4,7,... (diff is 3)
Thread-2 prints 2,5,8,...
Thread-3 prints 3,6,9,...
And the final output should be:
1,2,3,4,5,6,7,8,9,...
Here's the code that works wonderfully well:
package threadAlgo;
public class ControlOrder {
volatile Monitor monitor = new Monitor();
public static void main(String[] args) {
ControlOrder order = new ControlOrder();
Thread one = new Thread(new Task(order.monitor, 1));
one.setName("Thread-1");
Thread two = new Thread(new Task(order.monitor, 2));
two.setName("Thread-2");
Thread three = new Thread(new Task(order.monitor, 3));
three.setName("Thread-3");
one.start();
two.start();
three.start();
}
}
class Monitor {
int threadNumber = 1;
}
class Task implements Runnable {
private Monitor monitor;
private int myThreadNumber;
private int currentCount;
Task(Monitor monitor, int myThreadNumber) {
this.monitor = monitor;
this.myThreadNumber = myThreadNumber;
this.currentCount = myThreadNumber;
}
#Override
public void run() {
while (true) {
while (monitor.threadNumber != myThreadNumber) {
synchronized (monitor) {
try {
monitor.wait(100); //DOESN'T WORK WITHOUT THE TIMEOUT!!!
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized (monitor) {
if (monitor.threadNumber == myThreadNumber) {
System.out.println(Thread.currentThread().getName() + ": " + currentCount);
currentCount = currentCount + 3;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (myThreadNumber == 3) {
monitor.threadNumber = 1;
} else {
monitor.threadNumber = myThreadNumber + 1;
}
monitor.notifyAll();
}
}
}
}
The only problem is that if I use wait() instead of wait(timeout), then the thread halts.
UPDATE:
Wait condition (while loop) should be inside synchronized block. A lesson for beginners, including me.
You should always
perform notifyAll/notify in conjunction with a change in state.
check the state change before using wait() in a loop.
If you call notify() and no wait() is waiting, then the signal is lost, so unless you check a state change, (or timeout) you can block forever waiting for a signal which doesn't change.
I am trying an example of multi threading in java. There was an example on multithreading Synchronization in Java Complete reference 7th Edition. The example works fine. but when i slightly add a line to create another thread of the same class this does not work. Could some please let me know why this is happening. The example is given below. The below code is a classic exacple of producer and consumer. Where there is a single producer it works fine when i have 2 producers then it will fail. It just puts till 15 and stops.
class Q {
int n;
boolean valueSet = false;
synchronized int get() {
while (!valueSet) {
try {
wait();
} catch (InterruptedException e) {
System.out.println("InterruptedException caught");
}
}
System.out.println("Got: " + n);
valueSet = false;
notify();
return n;
}
synchronized void put(int n) {
while (valueSet) {
try {
wait();
} catch (InterruptedException e) {
System.out.println("InterruptedException caught");
}
}
this.n = n;
valueSet = true;
System.out.println("Put: " + n);
notify();
}
}
class Producer implements Runnable {
Q q;
Producer(Q q) {
this.q = q;
new Thread(this, "Producer").start();
//new Thread(this, "Producer2").start();
}
public void run() {
int i = 0;
while (true) {
q.put(i++);
}
}
}
class Consumer implements Runnable {
Q q;
Consumer(Q q) {
this.q = q;
new Thread(this, "Consumer").start();
}
#Override
public void run() {
while (true) {
q.get();
}
}
}
public class PCFixed {
public static void main(String[] args) {
Q q = new Q();
Producer P1 = new Producer(q);
new Consumer(q);
Producer P2 = new Producer(q);
System.out.println("Press Control-C to stop.");
}
}
Q is written to only accept one value at a time. You need to change put to be a boolean method - it returns true if valueset is true and then proceeds as normal, and returns false if valueset is false and returns without doing anything. Then the methods calling put will need to keep retrying until they get a true response. This way multiple consumers can use the same Q object without interfering with each other.
A better solution if you're using multiple producers is to use a ConcurrentLinkedQueue, which is a thread-safe queue. The producers will offer integers to the queue, and the consumers will poll the queue for integers. Multiple producers can simultaneously offer integers without interfering with each other, and multiple consumers can simultaneously poll integers without interfering with each other.
The example of concurrency you provide uses a single boolean flag to check whether there is a signal or not.
So this is more of a Semaphore arrangement than a producer consumer arrangement. It is too simplistic to deal with an arbitrary number of Threads.
If you really want to use producer consumer you are going to need a queue that holds more than one item.
static final AtomicBoolean run = new AtomicBoolean(true);
static class Producer implements Runnable {
final BlockingQueue<String> blockingQueue;
public Producer(BlockingQueue<String> blockingQueue) {
this.blockingQueue = blockingQueue;
}
#Override
public void run() {
while (run.get()) {
blockingQueue.add("Value from " + Thread.currentThread().getName());
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
//doesn't matter.
}
}
}
}
static class Consumer implements Runnable {
final BlockingQueue<String> blockingQueue;
public Consumer(BlockingQueue<String> blockingQueue) {
this.blockingQueue = blockingQueue;
}
#Override
public void run() {
while (run.get()) {
final String item;
try {
item = blockingQueue.take();
} catch (InterruptedException ex) {
return;
}
System.out.println(item);
}
}
}
public static void main(String[] args) throws InterruptedException {
final LinkedBlockingQueue<String> lbq = new LinkedBlockingQueue<>();
final ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(new Consumer(lbq));
for (int i = 0; i < 10; ++i) {
executorService.submit(new Producer(lbq));
}
Thread.sleep(10000);
run.set(false);
executorService.shutdownNow();
}
This simple example uses a LinkedBlockingQueue to post events to and read events from.
The Producer puts Strings into the queue with it's own Thread name (they do this every 100ms). The Consumer takes from the queue and prints the String.
The queue is a BlockingQueue so the take method will block if the queue is empty.
You can easily change the number of Producers and Consumers by changing the loops that add items to the ExecutorService. Experiment, see how it works.
The AtomicBoolean flag allows the program to shutdown all the child processes spawned.
Replace each occurrence of notify with notifyAll.
Please copy the program below and try running in your IDE. It's a simple Produce Consumer implementation - it runs fine when I use one Producer and one Consumer thread but fails when using 2 each. Please let me know the reason why this program hangs or is there anything else wrong with it.
import java.util.LinkedList;
import java.util.Queue;
public class PCQueue {
private volatile Queue<Product> productQueue = new LinkedList<Product>();
public static void main(String[] args) {
PCQueue pc = new PCQueue();
Producer producer = new Producer(pc.productQueue);
Consumer consumer = new Consumer(pc.productQueue);
new Thread(producer, "Producer Thread 1").start();
new Thread(consumer, "Consumer Thread 1").start();
new Thread(producer, "Producer Thread 2").start();
new Thread(consumer, "Consumer Thread 2").start();
}
}
class Producer implements Runnable {
private Queue<Product> queue = null;
private static volatile int refSerialNumber = 0;
public Producer(Queue<Product> queue) {
this.queue = queue;
}
#Override
public void run() {
while (true) {
synchronized (queue) {
while (queue.peek() != null) {
try {
queue.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
queue.add(new Product(++refSerialNumber));
System.out.println("Produced by: "
+ Thread.currentThread().getName() + " Serial Number: "
+ refSerialNumber);
queue.notify();
}
}
}
}
class Consumer implements Runnable {
private Queue<Product> queue = null;
public Consumer(Queue<Product> queue) {
this.queue = queue;
}
#Override
public void run() {
while (true) {
synchronized (queue) {
while (queue.peek() == null) {
try {
queue.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Product product = queue.remove();
System.out.println("Consumed by: "
+ Thread.currentThread().getName() + " Serial Number: "
+ product.getSerialNumber());
queue.notify();
}
}
}
}
class Product {
private int serialNumber;
public Product(int serialNumber) {
this.serialNumber = serialNumber;
}
public int getSerialNumber() {
return serialNumber;
}
}
The problem is that you are using queue.notify() which will only wake up a single Thread waiting on the Queue. Imagine Producer 1 calls notify() and wakes up Producer 2. Producer 2 sees that there is something in the queue so he doesn't produce anything and simply goes back to the wait() call. Now both your Producers and Consumers are all waiting to be notified and nobody is left working to notify anyone.
To solve the problem in your code, use queue.notifyAll() to wake up every Thread blocked at a wait(). This will allow your consumers to run.
As a note, your implementation limits the queue to having at most one item in it. So you won't see any benefit from the second set of producers and consumers. For a better all around implementation, I suggest you look at BlockingQueue and use an implementation which can be bounded, for instance, the ArrayBlockingQueue. Instead of synchronizing and using wait/notify, simply use BlockingQueue.offer() and BlockingQueue.take().
instead of queue.notify() use queue.notifyAll()