value = value;
}
} ///:~
Now we can define an interface that must be implemented by any class
that wishes to have its invariance tested: Feedback
//: c13:Invariant.java
public interface Invariant {
InvariantState invariant();
} ///:~
736
Thinking in Java
www.BruceEckel.com
Before creating the generic “watcher” thread, note that some of the
examples in this chapter will not behave as expected on all platforms.
Many of the examples here attempt to show violations of single-threaded
behavior when multiple threads are present, and this may not always
happen2. Alternatively, an example may attempt to show that the
violation does not occur by attempting (and failing) to demonstrate the
violation. In these cases we’ll need a way to stop the program after a few
seconds. The following class does this by subclassing the standard library
Timer class: Feedback
//: c13:Timeout.java
// Set a time limit on the execution of a program
import java.util.*;
public class Timeout extends Timer {
public Timeout(int delay, final String msg) {
super(true); // Daemon thread
schedule(new TimerTask() {
public void run() {
System.out.println(msg);
System.exit(0);
}
}, delay);
}
} ///:~
The delay is in milliseconds, and the message will be printed if the
timeout expires. Note that by calling super(true), this is created as a
daemon thread so that if your program completes in some other way this
thread will not prevent it from exiting. The Timer.schedule( ) method
is given a TimerTask subclass (created here as an anonymous inner
class) whose run( ) is executed after the second schedule( ) argument
delay (in milliseconds) runs out. Using Timer is generally simpler and
clearer than writing the code directly with an explicit sleep( ). In
2 Some examples were developed on a dual-processor Win2K machine which would
immediately show collisions. However, the same example run on single-processor
machines might run for extended periods without demonstrating a collision—this is the
kind of scary behavior that makes multithreading difficult. You can imagine developing on a single-processor machine and thinking that your code is thread safe, then discovering
breakages as soon as it’s moved to a multiprocessor machine.
Chapter 13: Concurrency
737
addition, Timer is designed to scale to large numbers of concurrently
scheduled tasks (in the thousands), so it can be a very useful tool. Feedback
Now we can use the Invariant interface and the Timeout class in the
InvariantWatcher thread:
//: c13:InvariantWatcher.java
// Repeatedly checks to ensure invariant is not violated
public class InvariantWatcher extends Thread {
private Invariant invariant;
public InvariantWatcher(Invariant invariant) {
this.invariant = invariant;
setDaemon(true);
start();
}
// Stop everything after awhile:
public
InvariantWatcher(Invariant invariant, final int timeOut){
this(invariant);
new Timeout(timeOut,
"Timed out without violating invariant");
}
public void run() {
while(true) {
InvariantState state = invariant.invariant();
if(state instanceof InvariantFailure) {
System.out.println("Invariant violated: "
+ ((InvariantFailure)state).value);
System.exit(0);
}
}
}
} ///:~
The constructor captures a reference to the Invariant object to be tested,
and starts the thread. The second constructor calls the first constructor,
then creates a Timeout which stops everything after a desired delay—this
is used in situations where the program may not exit by violating an
invariant. In run( ), the current InvariantState is captured and tested,
and if it fails then the value is printed. Note that we cannot throw an
exception inside this thread because that would only terminate the thread,
not the program. Feedback
738
Thinking in Java
www.BruceEckel.com
Now AlwaysEven.java can be rewritten using the above framework:
//: c13:EvenGenerator.java
// AlwaysEven.java using the invariance tester
public class EvenGenerator implements Invariant {
private int i;
public void next() { i++; i++; }
public int getValue() { return i; }
public InvariantState invariant() {
int val = i; // Capture it in case it changes
if(val % 2 == 0)
return new InvariantOK();
else
return new InvariantFailure(new Integer(val));
}
public static void main(String args[]) {
EvenGenerator gen = new EvenGenerator();
new InvariantWatcher(gen);
while(true)
gen.next();
}
} ///:~
When defining the invariant( ) method, you must capture all the values
of interest into local variables. This way, you can return the actual value
you have tested, not one that may have been changed (by another thread)
in the meantime. Feedback
In this case, the problem is not that the object goes through a state that
violates invariance, but that methods can be called by threads while the
object is in that intermediate unstable state. Feedback
Colliding over resources
The worst thing that happens with EvenGenerator is that a client
thread might see it in an unstable intermediate state. The object’s internal