9.1 | // Filename: Counter.java /* Notice that the result of running this program may not be what you expect. Since both threads are working on full throttle it is possible that only one of the threads is granted CPU time. */ public class Counter implements Runnable { public static void main(String[] args) { Storage store = new Storage(); new Counter(store); new Printer(store); } Storage storage; Counter(Storage target) { storage = target; new Thread(this).start(); } public void run() { int i=0; while (true) { storage.setValue(i); i++; } } } class Printer implements Runnable { Storage storage; Printer(Storage source) { storage = source; new Thread(this).start(); } public void run() { while (true) { System.out.println(storage.getValue()); } } } class Storage { int value; void setValue(int i) { value = i; } int getValue() { return value; } } |
9.2 | // Filename: Counter.java /* Only the Storage class has been altered. */ /* No changes to this class */ public class Counter implements Runnable { public static void main(String[] args) { Storage store = new Storage(); new Counter(store); new Printer(store); } Storage storage; Counter(Storage s) { storage = s; new Thread(this).start(); } public void run() { int i=0; while (true) { storage.setValue(i); i++; } } } /* No changes to this class. */ class Printer implements Runnable { Storage storage; Printer(Storage s) { storage = s; new Thread(this).start(); } public void run() { while (true) { System.out.println(storage.getValue()); } } } /* This class now ensures that getting and setting are done in an alternating fashion. */ class Storage { int value; boolean isUnread = false; synchronized void setValue(int i) { ensureUnread(false); value = i; setUnread(true); } synchronized int getValue() { ensureUnread(true); setUnread(false); return value; } private void ensureUnread(boolean shouldHaveUnread) { while (shouldHaveUnread != isUnread) try { wait(); } catch (InterruptedException ie) {} } private void setUnread(boolean b) { isUnread = b; notify(); } } |