/* * (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: * P (producers) * C (consumers) * N (bufsize) * M (messages per producer) * DP (minimum delay for producers) * DC (minimum delay for consumers) * [--verbose] */ package csci3366sample.bbuf; /* ---- Main class for test program ---------------------------------- */ public class TestBoundedBuffer { /* Main program. */ public static void main(String[] args) { String usageMessage = ("Arguments:\n" + " nProducers nConsumers\n" + " bufsize msgs\n" + " minProducerDelay minnsumerDelay\n"+ " [--verbose]"); if (args.length < 6) { System.err.println(usageMessage); 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(usageMessage); System.exit(1); } boolean verbose = (args.length > 6) && args[6].equals("--verbose"); BoundedBuffer buffer = new BoundedBuffer(bufsize, verbose); 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 ------------------------------------------ */ 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; } /* 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 ------------------------------------------ */ 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; } /* Code for thread to run. */ public void run() { boolean producersDone = false; boolean endOfInput = false; while (!endOfInput) { 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()) endOfInput = true; else m = buffer.get(); } if (!endOfInput) 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"); } } /* ---- Other supporting classes ------------------------------------- */ 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 + ")"; } } 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); } /* * Sleep 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); } } }