From ExtremeProgrammingChallengeFourteenTheBug
This test exposes the error with the message "producer1 blocked". It's verbose and it's slow (takes seconds to execute), and I hate that it's not deterministic. It can also report false errors if the process doesn't get any CPU time for 200 msec.
public void testTwoProducersTwoConsumers() throws Exception { final int numberOfItems = 10000; final Watchdog watchdog = new Watchdog(); final BoundedBuffer buffer = new BoundedBuffer(1); Thread producer1 = new Producer(buffer, watchdog, numberOfItems); Thread producer2 = new Producer(buffer, watchdog, numberOfItems); Thread consumer1 = new Consumer(buffer, watchdog, numberOfItems); Thread consumer2 = new Consumer(buffer, watchdog, numberOfItems); producer1.start(); producer2.start(); consumer1.start(); consumer2.start(); watchdog.waitUntilInactive(); assert("producer1 blocked", !producer1.isAlive()); assert("producer2 blocked", !producer2.isAlive()); assert("consumer1 blocked", !consumer1.isAlive()); assert("consumer2 blocked", !consumer2.isAlive()); }Watchdog is how I detect that nothing is happening. It allows the test to work on faster or slower CPUs:
private static class Watchdog { private long msecOfLastReset; public Watchdog() { reset(); } public synchronized void reset() { msecOfLastReset = System.currentTimeMillis(); } public void waitUntilInactive() throws InterruptedException { while (msecSinceReset() < 200) Thread.sleep(200); } private synchronized long msecSinceReset() { return System.currentTimeMillis() - msecOfLastReset; } };The producers and consumers are about what you'd expect (I expect):
private abstract static class Actor extends Thread { protected BoundedBuffer buffer; private Watchdog watchdog; private int numberOfItems; public Actor(BoundedBuffer buffer, Watchdog watchdog, int numberOfItems) { this.buffer = buffer; this.watchdog = watchdog; this.numberOfItems = numberOfItems; } public void run() { try { for (int i = 0; i < numberOfItems; i++) { act(); watchdog.reset(); } } catch (InterruptedException e) { } } protected abstract void act() throws InterruptedException; }; private static class Producer extends Actor { public Producer(BoundedBuffer buffer, Watchdog watchdog, int numberOfItems) { super(buffer, watchdog, numberOfItems); } protected void act() throws InterruptedException { buffer.put(new Object()); } }; private static class Consumer extends Actor { public Consumer(BoundedBuffer buffer, Watchdog watchdog, int numberOfItems) { super(buffer, watchdog, numberOfItems); } protected void act() throws InterruptedException { buffer.take(); } };I added a new constructor to BoundedBuffer that takes the buffer size, as I wasn't able to reproduce the deadlock with the default buffer size of 4.
Also, my test for one producer and two consumers showed a deadlock, as did my test for two producers and one consumer. One producer and one consumer worked fine. When I changed the notify() to notifyAll(), all the tests passed. -- WayneConrad