0

I have many producers that producer/consumer objects of different types, e.g. ProducerOfX, ProducerOfY, ConsumerOfX, ConsumerOfY. The producer puts an an object (X or Y) onto a queue, and notifies its relevant consumer (ProducerOfX notifies ConsumerOfX only).

For this to work, I need an object to use as a lock. My question is, do I create an object, e.g. new X() and get both ProducerOfX and ConsumerOfX to call wait/notify on that?

There will be only one queue and I have written it to hold generic objects MyQueue. So, many producers and consumers share this queue. Objects of say X and Y be put on the single queue. If an X is on it, ConsumerOfX is awakened and removes X. Same with Y.

I wonder is it best for the queue to notify the consumer 'I now have an X object' or is it better for the producer to say 'Hey, consumers of X, I have just put an X in the queue'. I'm guessing that the producers for a queue should not know about the queue's consumers.

Is there a cleaner way to do this?

TheCoder
  • 8,413
  • 15
  • 42
  • 54
  • Usually you use a concurrent queue implementation that takes care of both the locking (safe publishing, really) as well as the notification side of things. – BeeOnRope Nov 20 '16 at 16:59
  • The task I am trying to undertake is to teach me multithreading and therefore I'm not using anything from the concurrent package. – TheCoder Nov 20 '16 at 18:10
  • Your question is unclear. If you have different object types and different consumers, use two queues. Perhaps you should break your question up into at least two: one focusing on how to build a concurrent queue, and one for your idea of having different produced/consumer types interact with one queue. They seem orthogonal to me. – BeeOnRope Nov 20 '16 at 23:44

1 Answers1

0

It depends on the implementation. If there are separate queues for different products then each queue must be protected with a separate lock object (this lock object may the queue itself).

Also it depends if you are writing the implementation for the queue then you may decide to have the Lock object as a private variable. On addition you may check if the queue is full you may opt to have wait on the private lock variable. Once any object is consumed, you can invoke notifyall (or notify, though notifyall is preferable) on the private lock object. Similar way you can have wait and notify logic if consumers invoke the method to fetch an element from the queue when it is empty. This way your queue class will be responsible for locking and notifying using the private variable object. As queue has private variable lovk object hence each queue instance will have its own separate Lock object.

Other way is if the queue is general queue, may be its not yo u who is writing the queue, then you will need to protect the code invoking the methods for adding and fetching. In case you have separate queues for different products (x , y , etc.) then you need different Lock objects for each queue. This is needed to prevent deadlocks, it may happen ( if we do not have sepatrate lock objects) a consumerX is waiting for queueX to have a element inserted (as its empty) and another producerY is not getting opportunity to insert in queueY (as its full). Hence you need separate lock objects.

Update

@TheCoder If you have only one producer and consumer both intrested in same type of product then one queue is ok. Now comes the question of Shared object on which both should communicate. It depends upon the implementation, if you wish Queue to take care of it then Queue can have a private field private Object monitor = new Object(); and can have the enqueue and dequeue method being synchronized on `monitor'.

In dequeue method If queue is empty then call monitor.wait() inside the while loop until the queue is empty. If queue is not empty then remove the object from queue and call monitor.notifyAll();

In enqueue method if queue is full call monitor.wait() in while loop until queue is full. If queue is not full then add an object in queue and call monitor.notifyAll();

If your implementation is such that you wish queue not to take care of syncronization then you should have a shared object on which Producrer and Consumer can synchronize before invoking enqueue and dequeue on the queue. This shared object can be the instance of queue itself. The wait and notifyAll will need to be invoked inside the synchronized block on the shared object.

nits.kk
  • 5,204
  • 4
  • 33
  • 55
  • One queue. See my Edit. – TheCoder Nov 20 '16 at 18:38
  • One queue shared between various consumers and producers. Ok now Do you have separate consumers for different products or consumer and producers are also generic (any consumer can consume product X and any producer can produce Y) ? – nits.kk Nov 20 '16 at 19:17
  • Having single queue may not be a very good idea, it may happen producer of a specific product never or rarely gets chance to produce. Same may happen with a consumer of a specific product... – nits.kk Nov 20 '16 at 19:21
  • 1
    If you have disjoint producers (X and Y type) and disjoint consumers (X and Y type) it just doesn't make sense to have one queue. Logically, a queue has a single head element. If that happens to be a Y type and an X consumer wants to dequeue, what do you do? Logically you just have two queues, an X queue and a Y queue. If you want to wrap them in one object and have `enqueueY()` and `enqueueX()` methods, go ahead, but under the covers you need two queues. – BeeOnRope Nov 20 '16 at 23:44
  • This queue allows objects to be removed from the anywhere (including middle) in the queue. The consumer only blocks if the object type it's interested in is not in the queue. – TheCoder Nov 21 '16 at 13:36
  • @TheCoder, it is ok and as per my understanding you iterate the queue and if the product is of type Consumer then consumer consumes it. Now suppose queue is full with products of type only X and producer of type Y wish to produce. It will keep on waiting as queue is full. Suppose thread scheduler does not decide to schedule Consumer of type X as there are consumers of both type X and Y and thread scheduler is free to decide which thread gets the chance to aqhire the lock. continued in my next comment due to limited chars limit.... – nits.kk Nov 21 '16 at 19:04
  • Consumer of Y if gets the chance to aquire the lock it will simply iterate the queue and not finding any object it will again go to wait. Now as consumption and production both are at halt so there is no notifyall or notify method being invoked so system will be dead till this takes place. Though some time Consumer of type X may get chance but when it is not guaranteed, hence separate lock objects are needed for different products. – nits.kk Nov 21 '16 at 19:04
  • @nits.kk I see your point. My task says to create a thread safe bounded queue so I am wary of creating multiple queue. It might make my task easier by restricting producers to only produce the same type of object, e.g. an Integer, and to fulfill the consumers registering an interest requirement, I could insist on consumers indicating they integer they require, e.g. Consumer1 is only interested when new Integer(5) comes onto the queue. – TheCoder Nov 22 '16 at 10:38
  • @TheCoder, I have updated my answer, Please let us know if it helps. – nits.kk Nov 30 '16 at 13:42