Suggestions on my implementation of Producer Consumer - java

I am naive in multi-threading and is trying to learn it's concepts. This is my implementation for Producer-Consumer problem. Please have a look and suggest me if it is incorrect/crude/any other suggestions that could improve my design.
static int data = 0;
static Object obj1 = new Object();
static class Producer implements Runnable {
public void run() {
produce();
}
void produce() {
while (true) {
if (data < 5){
synchronized(obj1){
System.out.println("Producing Data. Now Data is "+data++);
obj1.notifyAll();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else{
try {
System.out.println("Producer inactive");
synchronized(obj1){
obj1.wait();
}
System.out.println("Producer active");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
static class Consumer implements Runnable{
public void run(){
consume();
}
void consume() {
while (true) {
if (data > 0){
synchronized(obj1){
System.out.println("Consuming Data. Now Data is "+data--);
obj1.notifyAll();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else{
try {
System.out.println("Consumer Inactive");
synchronized(obj1){
obj1.wait();
}
System.out.println("Consumer Active");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}

Ok several points. Producer and Consumer usually share a data structure. The use of the static data is very odd and quite frankly makes no sense. Typically what you'll want to share is a data structure like a queue between producer and consumer. The producer will add things on to the tail of the queue and the consumer(s) will draw things from the head of the queue (FIFO - first in first out). Right now I see none of that so what exactly is it producing vs consuming?
A good producer consumer architecture doesn't care too much about what type of data is exchanged so you can pass many different types of things over it. That's where object oriented command architecture will help you out. In this example SomeMessage represents the root of some object hierarchy so a variety of messages can be exchanged.
Here is a simple example of how you should instantiate a Producer-Consumer architecture in your program:
public class SomeClient {
public void start() {
Queue sharedQueue = new LinkedList();
producer = new Producer( sharedQueue );
consumer = new Consumer( sharedQueue );
producer.start();
consumer.start();
}
}
Here is the implementation of that:
public class Producer implements Runnable {
Thread thread;
Queue queue;
public Producer(Queue queue) {
this.queue = queue;
}
public void start() {
thread = new Thread(this);
thread.start();
}
public void shutdown() {
thread.interrupt(); // request a shutdown
thread.join(); // make sure we wait until Producer.thread exits before this thread continues
}
public void run() {
try {
while( !Thread.isInterrupted() ) {
SomeMessage message = produceAMessage();
synchronized( queue ) {
queue.add( message );
queue.notifyAll();
}
}
} catch( InterruptedException ex ) {
System.out.println("Producer shutting down per request.");
} finally {
thread = null;
}
}
}
public class Consumer implements Runnable {
Thread thread;
Queue queue;
public Consumer( Queue queue ) {
this.queue = queue;
}
public void start() {
thread = new Thread( this );
thread.start();
}
public void shutdown() {
thread.interrupt(); // request a shutdown
thread.join(); // make sure we wait until Consumer.thread exits before this thread continues
}
public void run() {
try {
while( !thread.isInterrupted() ) {
SomeMessage message = take();
doSomethingWithMessage( message );
}
} catch( InterruptedException ex ) {
System.out.println("Stop processing - consumer per request.");
} finally {
thread = null;
}
}
private SomeMessage take() throws InterruptedException {
synchronized( queue ) {
queue.wait();
return queue.remove();
}
}
}
A couple of things that differ in this implementation. Producer and Consumer share a Queue instance and they use that instance to perform synchronized calls on. That way neither write or read from that structure without owning the lock. After they have either added to the queue (producer) or removed from the queue (consumer) they are free from needing to use synchronization. They are free to process without needing to communicate with each other. They trade instances of SomeMessage between each instance by adding to the tail and drawing from the head.
The take() method is very important in this code. Without the helper method you can't process the message AND release the lock. This important so that your Consumer can receive a message and let go of the lock to allow other Producers/Consumers to add/remove messages while this particular Consumer is processing a message. This keeps throughput as fast as possible.
And yes I said Producers. This architecture allows for multiple Producers AND multiple Consumers without needing to change the internals of either Producer or Consumer.
Notice that catching InterruptedException is outside the while loop. This is very important if you want a predictable program that shuts down cleanly. An InterruptedException and interrupted concept is the heart of well behaving Java threads. If you don't know under what conditions this exception is generated you'll never understand multi-threaded apps in Java. It's not a random occurrence. Java threads can't be stopped programatically. Another thread must request it to interrupt itself. And the thread must obey the request or else it won't stop. So if we get one. Shutdown. In this program we'll only get it when we call wait or notify which means while we're processing a message we won't be interrupted. Consumers will finish processing messages before halting.
Finally, it's actually much easier to implement a Producer-Consumer relationship given the concurrency libraries in Java, but this is a good example of how you do it at the lowest level of Java to understand what those libraries are doing for you.

Encapsulating the consume and produce behaviors could be more more reusable. In the code below I decoupled the shared resource synchronization issues from consumer/producer thread which could be useful in solving similar problems like Object Pool and Connection Pool.
import java.util.LinkedList;
import java.util.Queue;
public class ProducerConsumer {
public static void main(String[] args) {
SyncQueue syncQueue = new SyncQueue(1);
Producer producer = new Producer(syncQueue , 10);
Consumer consumer = new Consumer(syncQueue,10);
producer.start();
consumer.start();
}
}
class SyncQueue {
private Queue<Integer> queue = new LinkedList<Integer>();
private Integer size;
public SyncQueue(Integer size) {
super();
this.size = size;
this.signalledBefore = false;
}
public synchronized void put(Integer data){
while(queue.size() == size){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.add(data);
notifyAll();
}
public synchronized Integer get(){
while(queue.isEmpty()){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Integer data = queue.remove();
notifyAll();
return data;
}
}
class Producer extends Thread{
private SyncQueue syncQueue;
private Integer size;
public Producer(SyncQueue syncQueue, Integer size) {
this.syncQueue = syncQueue;
this.size = size;
}
#Override
public void run() {
for (Integer i = 0; i < size; i++) {
syncQueue.put(i);
System.out.println("Produced:" + i);
try {
sleep((int)Math.random()*100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer extends Thread{
private SyncQueue syncQueue;
private Integer size;
public Consumer(SyncQueue syncQueue, Integer size) {
this.syncQueue = syncQueue;
this.size = size;
}
#Override
public void run() {
for (Integer i = 0; i < size; i++) {
try {
sleep((int)Math.random()*100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Consumed:" + syncQueue.get());
}
}
}

Related

Threads producer consumer in java

Below is the consumer producer problem code, but the code is not working as expected. Here the consumer and producer are supposed to be just producing and consuming one object.
public class ProducerConsumer {
private static LinkedList<Integer> linkedList = new LinkedList<>();
public static void main(String a[]) throws InterruptedException {
Thread producer = new Thread(new Runnable() {
#Override
public void run() {
synchronized(this) {
while (linkedList.size() == 1) {
try {
wait();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Produced");
linkedList.add(1);
notify();
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread consume = new Thread(new Runnable() {
#Override
public void run() {
// produce
synchronized(this) {
while (linkedList.isEmpty()) {
try {
wait();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Consumed");
linkedList.removeFirst();
notify();
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
});
producer.start();
consume.start();
producer.join();
consume.join();
}
}
We get the output as : Produced
And the program hangs.
Please help with possible solutions/ explanations
Use a shared lock. In the posted code each Runnable is using itself as a lock so no actual locking takes place.
When a thread waits, another thread needs to call notify on the same lock in order to wake up the waiting thread. We know from your logging that the Producer thread does its thing, but since the notify acts on a lock that is not the same as the one the Consumer is using, the consumer thread never wakes up.
Changing the code to use a shared lock works:
import java.util.*;
public class ProducerConsumer { private static LinkedList linkedList = new LinkedList();
public static void main(String a[]) throws InterruptedException {
final Object lock = new Object();
Thread producer = new Thread(new Runnable() {
#Override
public void run() {
synchronized (lock) {
while (linkedList.size() ==1) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Produced");
linkedList.add(1);
lock.notify();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread consume = new Thread(new Runnable() {
#Override
public void run() {
// produce
synchronized (lock) {
while (linkedList.isEmpty()) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Consumed");
linkedList.removeFirst();
lock.notify();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
producer.start();
consume.start();
producer.join();
consume.join();
}
}
Output for this is:
c:\example>java ProducerConsumer
Produced
Consumed
which I think is what you're expecting.
Btw see this other answer I wrote for a dirt-simple implementation of a queue; you are better off protecting the shared data structure than putting the code in the threads accessing the data structure, especially look at how much easier the code is to write.
Concurrency means that you can not know before runtime which Thread will end first. So you can not know which of the Consumer and Producer is launched, executed or finished first.
To help you, you can use a cyclic barrier https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html or applying the Fork/Join Framework https://docs.oracle.com/javase/tutorial/essential/concurrency/forkjoin.html
Your synchronized blocs just say : only one Thread at a time can execute this part of code, not execute the first and the second after.
An example of how CyclicBarrier works :
service = Executors.newFixedThreadPool(numThreadsTotal);
CyclicBarrier c = new CyclicBarrier(numThreadsToWait);
runProducer();
c.await();
runConsumer();
It will wait until the there is as much Threads as numThreadsToWait that have execute the runProducer to execute the runConsumer().
Perhaps using a Thread Pool with a size of 1 could help you, but you will loose the benefits of concurrency.
I think best what you can do, is use BlockingQueue.

Producer consumer problems without semaphores in java threads synchronization

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.

Multithreading Synchronization not working in Java

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.

Can't stop producer/consumer threads with Poison Pill

I have very simple code which simulates Producer/Consumer stop technique using "Poison Pill".
I have Producer class:
public class Producer extends Thread {
private final BlockingQueue<String> queue;
public Producer(BlockingQueue<String> queue) {
this.queue = queue;
}
#Override
public void run() {
try {
while (true) {
//unblocking this line will cause the code to stop after intrrupt
//System.out.println("1");
queue.put("hello world");
}
} catch (InterruptedException e) {
try {
queue.put(Main.POISON_PILL);
} catch (InterruptedException e1) {
}
}
}
}
Consumer Class:
public class Consumer extends Thread {
private final BlockingQueue<String> queue;
public Consumer(BlockingQueue<String> queue) {
this.queue = queue;
}
#Override
public void run() {
try {
while (true) {
String s = queue.take();
if (s.equals(Main.POISON_PILL))
break;
else
System.out.println(s);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
Now the main function:
public static String POISON_PILL = "POISON_PILL";
public static void main(String[] args) {
BlockingQueue<String> queue = new LinkedBlockingQueue<String>();
Producer producer = new Producer(queue);
Consumer consumer = new Consumer(queue);
producer.start();
consumer.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
} finally {
producer.interrupt();
}
}
For not known reason even after producer.interrupt() called,
"hello world" keeps printing in console forever.
Second thing that I can't understand is why uncommenting System.out.println("1"); will cause program to exit after the producer thread interrupted.
Please help me to understand why.
My guess is that your producer simply runs so much faster than your consumer that you appear never to run out of items. Creating a LinkedBlockingQueue without an explicit capacity creates one with capacity Integer.MAX_VALUE, which is enough for the consumer to keep printing for quite a while.
That's also probably the reason why it starts working when you add the System.out.println line; by requiring console I/O, it slows down the producer to the point where the consumer is able to keep up.
Try creating a LinkedBlockingQueue with some small capacity, like 100 or so, instead.

My Produce Consumer Hangs

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

Categories

Resources