// // example of threads synchronizing with wait / notify -- // multiple producer and consumer threads sharing a bounded // message buffer. producer threads put messages into the // buffer, blocking if it's full, while consumer threads remove // messages from the buffer, blocking if it's empty. // // see main method below for command-line arguments. // import java.util.* ; public class ProducerConsumer { // command-line arguments are integers P, C, B, S, N: // P is number of producer threads // C is number of consumer threads // B is maximum size of message buffer // S is number of milliseconds consumer should sleep // between consuming messages. // N is total number of messages to process. // creates P producer threads and C consumer threads, all // sharing a single message buffer (as described above, // maximum size B). each producer thread produces a // message every second (unless blocked); each consumer // thread consumes a message every S milliseconds (unless // blocked). // prints consumed messages plus informational messages // showing when processes are waiting in putMsg()/getMsg(). // runs until N total messages have been sent and received. public static void main(String[] args) { if (args.length < 5) { System.out.println("Arguments are:") ; System.out.println(" number of producers") ; System.out.println(" number of consumers") ; System.out.println(" size of message buffer") ; System.out.println(" sleep interval for consumer") ; System.out.println(" total number of messages") ; System.exit(-1) ; } int P = Integer.parseInt(args[0]) ; int C = Integer.parseInt(args[1]) ; int B = Integer.parseInt(args[2]) ; int S = Integer.parseInt(args[3]) ; int N = Integer.parseInt(args[4]) ; MessageBuffer msgbuf = new MessageBuffer(B, N) ; Thread[] pthreads = new Thread[P] ; Thread[] cthreads = new Thread[C] ; // create objects, threads // producers for (int i = 0 ; i < P ; i++) pthreads[i] = new Thread( new Producer(i, msgbuf)) ; // consumers for (int i = 0 ; i < C ; i++) cthreads[i] = new Thread( new Consumer(i, S, msgbuf)) ; // start them up for (int i = 0 ; i < C ; i++) cthreads[i].start() ; for (int i = 0 ; i < P ; i++) pthreads[i].start() ; } } // // a MessageBuffer object represents a bounded buffer shared among // producers and consumers. synchronization ensures that // producers cannot write into a full buffer nor consumers read // from an empty buffer. // class MessageBuffer { Vector msgbuf = new Vector() ; int maxNum ; // maximum number of elements in msgbuf int msgsSent ; // messages sent int msgsReceived ; // messages received int totalToProcess ; // total messages to be sent/received MessageBuffer(int b, int n) { maxNum = b ; msgsSent = 0 ; msgsReceived = 0 ; totalToProcess = n ; } // adds a message to the buffer (blocking if it's already // full) // returns true if message was added (msgsSent less than // totalToProcess), false otherwise synchronized boolean putMsg(String s) { if (msgsSent == totalToProcess) { System.out.println("returning 'done' " + "in putMsg()") ; return false ; } // delay if no room in buffer while (msgbuf.size() == maxNum) { System.out.println("Waiting in putMsg()") ; try { wait() ; } catch (InterruptedException e) { // no threads in this program use // interrupt(), so no need to do // anything here } } // add message msgbuf.addElement(s + ", message " + msgsSent++) ; // wake up all threads, in case there are waiting // consumers notifyAll() ; return true ; } // removes a message to the buffer (blocking if it's empty) // returns message if one was retrieved (msgsReceived less than // totalToProcess), null reference otherwise synchronized String getMsg() { String rVal ; if (msgsReceived == totalToProcess) { System.out.println("returning 'done' " + "in getMsg()") ; return null ; } // delay if no messages to retrieve while (msgbuf.size() == 0) { System.out.println("Waiting in getMsg()") ; try { wait() ; } catch (InterruptedException e) { // no threads in this program use // interrupt(), so no need to do // anything here } } // remove message rVal = (String) msgbuf.firstElement() ; msgbuf.removeElementAt(0) ; msgsReceived++ ; // wake up all threads, in case there are waiting // producers notifyAll() ; return rVal ; } } // // a Producer object adds objects to the shared bounded buffer, // sleeping for 1 second between adds (so messages, which include // a timestamp, will all be different). // class Producer implements Runnable { private int id ; private MessageBuffer msgbuf ; // constructor Producer(int id, MessageBuffer msgbuf) { this.id = id ; this.msgbuf = msgbuf ; } // method to be invoked when thread is started public void run() { boolean cont = true ; do { cont = msgbuf.putMsg("Producer " + id + ", " + new Date()) ; try { Thread.sleep((long) 1000) ; } catch (InterruptedException e) { // no threads in this program use // interrupt(), so no need to do // anything here } } while (cont) ; } } // // a Consumer object removes objects from the shared bounded buffer // and prints them, sleeping for s seconds between operations // class Consumer implements Runnable { private int id, s ; private MessageBuffer msgbuf ; // constructor Consumer(int id, int s, MessageBuffer msgbuf) { this.id = id ; this.s = s ; this.msgbuf = msgbuf ; } // method to be invoked when thread is started public void run() { String msg = "" ; do { msg = msgbuf.getMsg() ; if (msg != null) System.out.println("Consumer " + id + " received: " + msg) ; try { Thread.sleep((long) s) ; } catch (InterruptedException e) { // no threads in this program use // interrupt(), so no need to do // anything here } } while (msg != null) ; } }