/*********************************************************************** * Tom Kelliher * CS 245 * Synch2.java * * This applet is the second pass at demonstrating the classic producer * consumer problem. The producer produces 10 integer values and * forms their sum. These values are stored in an intermediate queue * of depth one implemented as a monitor, with full synchronization * control. The values are consumed by the consumer, which also forms * their sum. In this model, it is assumed that the producer needs * 1000 ms. to produce a value and the consumer needs 2000 ms. to consume * a value. * * Comments below point out differences between this applet and * Synch1.java. ***********************************************************************/ import java.applet.*; import java.awt.*; /*********************************************************************** * class Synch2 ***********************************************************************/ public class Synch2 extends Applet { Display display = new Display(); //Display d = new Display(); Queue que = new Queue(0, display); Producer producer = new Producer(display, que); //Producer p = new Producer(d, que); Consumer consumer = new Consumer(display, que); public void init() { add(display); //add(d); producer.start(); //p.start(); consumer.start(); } } /*********************************************************************** * class Display --- Used to display current produced/consumed value as * well as the producer's and consumer's sums. ***********************************************************************/ class Display extends Panel { private Label producer = new Label("Producer"); private Label consumer = new Label("Consumer"); private Label current = new Label("Current Value"); private Label sum = new Label("Sum"); public Label prodCur = new Label("Empty"); public Label prodSum = new Label("Empty"); public Label conCur = new Label("Empty"); public Label conSum = new Label("Empty"); public Display() { setLayout(new GridLayout(3, 3)); add(new Panel()); add(producer); add(consumer); add(current); add(prodCur); add(conCur); add(sum); add(prodSum); add(conSum); } } /*********************************************************************** * class Producer --- Producer thread. Display d is the Display object * to use for displaying sum. Queue q is the queue to which * production is stored. ***********************************************************************/ class Producer extends Thread { private Display disp; private Queue que; public Producer(Display d, Queue q) { disp = d; que = q; } public void run() { int i; int sum = 0; for (i = 0; i < 10; ++i) { sum += i; disp.prodSum.setText("" + sum); que.put(i); try { sleep(1000); } catch (Exception e) { } } disp.prodSum.setText("Final: " + sum); } } /*********************************************************************** * class Consumer --- Consumer thread. Display d is the Display object * to use for displaying sum. Queue q is the queue from which * we consume. ***********************************************************************/ class Consumer extends Thread { private Display disp; private Queue que; public Consumer(Display d, Queue q) { disp = d; que = q; } public void run() { int i; int val; int sum = 0; while (true) { try { sleep(2000); } catch (Exception e) { } val = que.get(); sum += val; disp.conSum.setText("" + sum); } } } /*********************************************************************** * class Queue --- The depth one storage buffer between producer and * consumer. int v is the initial stored value, which is now * unnecessary because of the explicit synchronization. Display d is * the display used for displaying the produced or consumed values. ***********************************************************************/ class Queue { private int val; private Display disp; private boolean full = false; private boolean empty = true; public Queue(int v, Display d) { val = v; disp = d; } // Produce into the queue. /* No matter how many threads are calling an object's methods, only * one thread at a time can execute _any_ of the synchronized methods. * This is due to the requirement that a thread must obtain the * object's lock before executing a synchronized method. */ public synchronized void put(int v) { /* If the queue is full, wait for a signal that it _may_ be * empty, and try again. */ while (full) try { wait(); } catch (Exception e) { } disp.prodCur.setText("" + v); val = v; /* Update the state of the queue and notify a waiting thread * that's it time to re-check its condition. */ full = true; empty = false; notify(); } // Consume from the queue. public synchronized int get() { /* If the queue is empty, wait for a signal that it _may_ be * full, and try again. */ while (empty) try { wait(); } catch (Exception e) { } disp.conCur.setText("" + val); /* Update the state of the queue and notify a waiting thread * that's it time to re-check its condition. */ full = false; empty = true; /* You might think that there's a race condition here, because * we're waking a waiting thread before consuming the value. * However, as we still hold the lock, no producer can produce * until we consume. */ notify(); return val; } }