I was doing a research in producers and consumer design patterns with regards to threads in java, I recently explored in java 5 with the introduction With introduction of Bl
If you want to understand how a BlockingQueue works, for educational purposes, you can always have a look on its source code.
The simplest way could be to synchronize
the offer()
and take()
methods, and once the queue is full and someone is trying to offer()
an element - invoke wait()
. When someone is taking an element, notify()
the sleeping thread. (Same idea when trying to take()
from an empty queue).
Remember to make sure all your wait()
calls are nested in loops that checks if the conditions are met each time the thread is awakened.
If you are planning to implement it from scratch for product purposes - I'd strongly argue against it. You should use an existing, tested libraries and components as much as possible.
I can do this wait-notify stuff in my sleep (or at least I think I can). Java 1.4 source provided beautiful examples of all this, but they've switched to doing everything with atomics and it's a lot more complicated now. The wait-notify does provide flexibility and power, though the other methods can shield you from the dangers of concurrency and make for simpler code.
To do this, you want some fields, like so:
private final ConcurrentLinkedQueue<Intger> sharedQueue =
new ConcurrentLinkedQueue<>();
private volatile boolean waitFlag = true;
Your Producer.run would look like this:
public void run() {
for (int i = 0; i < 100000, i++) {
System.out.println( "Produced: " + i );
sharedQueue.add( new Integer( i ) );
if (waitFlag) // volatile access is cheaper than synch.
synchronized (sharedQueue) { sharedQueue.notifyAll(); }
}
}
And Consumer.run:
public void run() {
waitFlag = false;
for (;;) {
Integer ic = sharedQueue.poll();
if (ic == null) {
synchronized (sharedQueue) {
waitFlag = true;
// An add might have come through before waitFlag was set.
ic = sharedQueue.poll();
if (ic == null) {
try { sharedQueue.wait(); }
catch (InterruptedException ex) {}
waitFlag = false;
continue;
}
waitFlag = true;
}
}
System.out.println( "Consumed: " + ic );
}
}
This keeps synchronizing to a minimum. If all goes well, there's only one look at a volatile field per add. You should be able to run any number of producers simultaneously. (Consumer's would be trickier--you'd have to give up waitFlag
.) You could use a different object for wait/notifyAll.
If you want to know another way to do this try using an ExecutorService
public static void main(String... args) {
ExecutorService service = Executors.newSingleThreadExecutor();
for (int i = 0; i < 100; i++) {
System.out.println("Produced: " + i);
final int finalI = i;
service.submit(new Runnable() {
@Override
public void run() {
System.out.println("Consumed: " + finalI);
}
});
}
service.shutdown();
}
With just 10 tasks the producer can be finished before the consumer starts. If you try 100 tasks you may find them interleaved.