// // (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. // Consumer threads count how many buffer elements have been // consumed in all. // Main program waits for producers to finish and for the above // count to become zero, upon which it cancels the consumers. // // Command-line arguments are P, C, N, and M. // // Notes: // We assume that calls to System.out.println() are atomic. // ---- Main class for test program ---------------------------------- public class TestBoundedBuffer { // Instance variables. Producer [] producers; // objects for producer threads Consumer [] consumers; // objects for consumer threads BoundedBuffer buffer; SharedInt msgsRemaining; // total messages to be produced, // minus count of messages consumed // Class variables (to be used by method randomDelay()). private static int minDelay = 500; private static int maxDelay = 1000; private static java.util.Random rand = new java.util.Random(1); // Constructor. public TestBoundedBuffer(int nProducers, int nConsumers, int bufSize, int nMsgs) { buffer = new BoundedBuffer(bufSize); msgsRemaining = new SharedInt(nProducers * nMsgs); producers = new Producer[nProducers]; for (int i = 0; i < nProducers; ++i) producers[i] = new Producer(i, nMsgs, buffer); consumers = new Consumer[nConsumers]; for (int i = 0; i < nConsumers; ++i) consumers[i] = new Consumer(i, buffer, msgsRemaining); } // Method to actually run test. public void runTest() { ThreadManager ptMgr = new ThreadManager(producers); ThreadManager ctMgr = new ThreadManager(consumers); ptMgr.start(); ctMgr.start(); ptMgr.join(); ctMgr.join(); System.out.println("That's all, folks!"); } // Method to wait for a randomly-generated interval. // Allows exceptions thrown by Thread.sleep() to bubble up. public static void randomDelay() throws InterruptedException { int d = (int) (rand.nextDouble()*(maxDelay - minDelay) + minDelay); Thread.sleep(d); } // Main program. public static void main(String[] args) { if (args.length < 4) { System.err.println("Arguments: " + "nProducers nConsumers bufsize msgs"); System.exit(1); } int nProducers = Integer.parseInt(args[0]); int nConsumers = Integer.parseInt(args[1]); int bufsize = Integer.parseInt(args[2]); int msgs = Integer.parseInt(args[3]); TestBoundedBuffer o = new TestBoundedBuffer(nProducers, nConsumers, bufsize, msgs); o.runTest(); } } // ---- Class for producers ------------------------------------------ // Each producer thread has one object of this class. class Producer implements Runnable { // Instance variables. private int myID; private int msgs; private BoundedBuffer buffer; // reference to shared variable // Constructor. Producer(int myID, int msgs, BoundedBuffer buffer) { this.myID = myID; this.msgs = msgs; this.buffer = buffer; } // Method containing code for thread to run. public void run() { try { // Insert messages with producer's ID # into buffer. for (int i = 0; i < msgs; ++i) { TestBoundedBuffer.randomDelay(); TestMessage m = new TestMessage(myID, i); buffer.put(m); System.out.println("Message " + m + " produced"); } } // Ignore these exceptions -- not generated/used in this pgm. catch (InterruptedException e) { } } } // ---- Class for consumers ------------------------------------------ // Each consumer thread has one object of this class. class Consumer implements Runnable { // Instance variables. private int myID; private BoundedBuffer buffer; // reference to shared variable private SharedInt msgsRemaining; // reference to shared variable // Constructor. Consumer(int myID, BoundedBuffer buffer, SharedInt msgsRemaining) { this.myID = myID; this.buffer = buffer; this.msgsRemaining = msgsRemaining; } // Method containing code for thread to run. public void run() { try { // Consume messages until no more. while (reserveMsg()) { TestBoundedBuffer.randomDelay(); // Get the next message and print. TestMessage m = (TestMessage) buffer.get(); System.out.println("Message " + m + " consumed by " + myID); } } // Ignore these exceptions -- not generated/used in this pgm. catch (InterruptedException e) { } } // Method to check for more messages to consume. // Decrements shared counter, in effect "reserving" a message // for the current thread to consume. // Returns true if counter was > 0 at entry, false otherwise. // Allows exceptions thrown by lock() to bubble up. private boolean reserveMsg() throws InterruptedException { msgsRemaining.lock(); boolean returnVal = (msgsRemaining.value > 0); if (returnVal) --msgsRemaining.value; msgsRemaining.unlock(); return returnVal; } } // ---- Class for messages for bounded-buffer test program ----------- class TestMessage { // Instance variables. private int producerID; private int msgNum; // Constructor. TestMessage(int producerID, int msgNum) { this.producerID = producerID; this.msgNum = msgNum; } // Method to convert message to printable string. public String toString() { return "(" + producerID + ", " + msgNum + ")"; } }