/* * (Somewhat) simple program demonstrating the use of a "bounded * buffer" class of size N, with P producer threads and C consumer * threads. * Each producer thread will insert into the buffer M "messages" * (pairs of integers); consumer threads will read from the buffer * and print. * Consumers stop when they detect that the producers are all done and the * buffer is empty. Detection that the producers are done is accomplished by * having the main program interrupt the consumers. This is not necessarily * the best approach, but illustrates the use of interrupts. * * Command-line arguments are P (producers), C (consumers), N (bufsize), * M (messages per producer), DP (minimum delay for producers), * DC (minimum delay for consumers). * * Notes: * Short output goes to System.out, more verbose output to System.err. * We assume that calls to System.out.println() and System.err.println() * are atomic. */ /* ---- Main class for test program ---------------------------------- */ public class TestBoundedBuffer { /* Main program. */ public static void main(String[] args) { if (args.length < 6) { System.err.println("Arguments: " + "nProducers nConsumers bufsize msgs " + "minProducerDelay minConsumerDelay"); System.exit(1); } int nProducers = 0; int nConsumers = 0; int bufsize = 0; int msgs = 0; int minPdelay = 0; int minCdelay = 0; try { nProducers = Integer.parseInt(args[0]); nConsumers = Integer.parseInt(args[1]); bufsize = Integer.parseInt(args[2]); msgs = Integer.parseInt(args[3]); minPdelay = Integer.parseInt(args[4]); minCdelay = Integer.parseInt(args[5]); } catch (NumberFormatException e) { System.err.println("Arguments: " + "nProducers nConsumers bufsize msgs " + "minProducerDelay minConsumerDelay"); System.exit(1); } BoundedBuffer buffer = new BoundedBuffer(bufsize); RandomDelayer pDelayer = new RandomDelayer(minPdelay); RandomDelayer cDelayer = new RandomDelayer(minCdelay); /* create producer, consumer threads */ Thread[] producers = new Thread[nProducers]; for (int i = 0; i < producers.length; ++i) { producers[i] = new Thread(new Producer(i, msgs, buffer, pDelayer)); } Thread[] consumers = new Thread[nConsumers]; for (int i = 0; i < consumers.length; ++i) { consumers[i] = new Thread(new Consumer(i, buffer, cDelayer)); } /* start all threads */ for (int i = 0; i < producers.length; ++i) { producers[i].start(); } for (int i = 0; i < consumers.length; ++i) { consumers[i].start(); } /* wait for producers to finish */ try { for (int i = 0; i < producers.length; ++i) { producers[i].join(); } } catch (InterruptedException e) { System.err.println("this should not happen"); } /* interrupt consumers (so they know producers are done) */ for (int i = 0; i < consumers.length; ++i) { consumers[i].interrupt(); } /* wait for consumers to finish */ try { for (int i = 0; i < consumers.length; ++i) { consumers[i].join(); } } catch (InterruptedException e) { System.err.println("this should not happen"); } System.out.println("That's all, folks!"); } /* ---- Class for producers ------------------------------------------ */ /* Each producer thread has one object of this class. */ private static class Producer implements Runnable { private int myID; private int msgs; private BoundedBuffer buffer; private RandomDelayer delayer; public Producer(int myID, int msgs, BoundedBuffer buffer, RandomDelayer delayer) { this.myID = myID; this.msgs = msgs; this.buffer = buffer; this.delayer = delayer; } /* Method containing code for thread to run. */ public void run() { try { for (int i = 0; i < msgs; ++i) { delayer.delay(); TestMessage m = new TestMessage(myID, i); buffer.put(m); System.out.println("Message " + m + " produced"); } } catch (InterruptedException e) { System.err.println("this should not happen"); } System.out.println("Producer " + myID + " done"); } } /* ---- Class for consumers ------------------------------------------ */ /* Each consumer thread has one object of this class. */ private static class Consumer implements Runnable { private int myID; private BoundedBuffer buffer; private RandomDelayer delayer; public Consumer(int myID, BoundedBuffer buffer, RandomDelayer delayer) { this.myID = myID; this.buffer = buffer; this.delayer = delayer; } /* Method containing code for thread to run. */ public void run() { boolean producersDone = false; boolean done = false; while (!done) { if (Thread.interrupted()) { producersDone = true; System.out.println("Consumer " + myID + " detected interrupted status"); } try { delayer.delay(); TestMessage m = null; /* synchronize here because otherwise we could get * stuck waiting to get something from a buffer that * has been emptied by another consumer. */ synchronized(buffer) { if (producersDone && buffer.isEmpty()) done = true; else m = (TestMessage) buffer.get(); } if (!done) System.out.println("Message " + m + " consumed by " + myID); } catch (InterruptedException e) { System.out.println("Consumer " + myID + " interrupted"); producersDone = true; } } System.out.println("Consumer " + myID + " done"); } } /* ---- Class for messages for bounded-buffer test program ----------- */ private static class TestMessage { private int producerID; private int msgNum; public TestMessage(int producerID, int msgNum) { this.producerID = producerID; this.msgNum = msgNum; } public String toString() { return "(" + producerID + ", " + msgNum + ")"; } } /* ---- Class for generating random delay times ---------------------- */ private static class RandomDelayer { private int minDelay; private int maxDelay; private java.util.Random rand; public RandomDelayer(int minDelay) { this.minDelay = minDelay; this.maxDelay = minDelay * 2; rand = new java.util.Random(1); } /* Method to wait for a randomly-generated interval. * Allows exceptions thrown by Thread.sleep() to bubble up. */ public void delay() throws InterruptedException { int d = (int) (rand.nextDouble()*(maxDelay - minDelay) + minDelay); Thread.sleep(d * 1000); } } }