Because there’s only one entity, you never have to think about the
problem of two entities trying to use the same resource at the same time,
problems like two people trying to park in the same space, walk through a
door at the same time, or even talk at the same time. Feedback
With multithreading, things aren’t lonely anymore, but you now have the
possibility of two or more threads trying to use the same limited resource
at once. Colliding over a resource must be prevented or else you’ll have
two threads trying to access the same bank account at the same time,
print to the same printer, adjust the same valve, etc. Feedback
Improperly accessing resources
Consider the following example, where the class “guarantees” that it will
always deliver an even number when you call getValue( ). However,
there’s a second thread named “Watcher” that is constantly calling
getValue( ) and checking to see if this value is truly even. This seems like
a needless activity, since looking at the code it appears obvious that the
value will indeed be even. But that’s where the surprise comes in. Here’s
the first version of the program: Feedback
//: c13:AlwaysEven.java
// Demonstrating thread collision over resources by
// reading an object in an unstable intermediate state.
public class AlwaysEven {
private int i;
public void next() { i++; i++; }
public int getValue() { return i; }
public static void main(String args[]) {
final AlwaysEven ae = new AlwaysEven();
new Thread("Watcher") {
public void run() {
while(true) {
int val = ae.getValue();
if(val % 2 != 0) {
System.out.println(val);
734
Thinking in Java
www.BruceEckel.com
System.exit(0);
}
}
}
}.start();
while(true)
ae.next();
}
} ///:~
In main( ), an AlwaysEven object is created—it must be final because it is accessed inside the anonymous inner class defined as a Thread. If
the value read by the thread is not even, it prints it out (as proof that it has
caught the object in an unstable state) and then exits the program. Feedback
This example shows a fundamental problem with using threads. You
never know when a thread might be run. Imagine sitting at a table with a
fork, about to spear the last piece of food on your plate and as your fork
reaches for it, the food suddenly vanishes (because your thread was
suspended and another thread came in and stole the food). That’s the
problem that you’re dealing with when writing concurrent programs.
Feedback
Sometimes you don’t care if a resource is being accessed at the same time
you’re trying to use it (the food is on some other plate). But for
multithreading to work, you need some way to prevent two threads from
accessing the same resource, at least during critical periods. Feedback
Preventing this kind of collision is simply a matter of putting a lock on a
resource when one thread is using it. The first thread that accesses a
resource locks it, and then the other threads cannot access that resource
until it is unlocked, at which time another thread locks and uses it, etc. If
the front seat of the car is the limited resource, the child who shouts
“Dibs!” asserts the lock. Feedback
A resource testing framework
Before going on, let’s try to simplify things a bit by creating a little
framework for performing tests on these types of threading examples. We
can accomplish this by separating out the common code that might
appear across multiple examples. First, note that the “watcher” thread is
Chapter 13: Concurrency
735
actually watching for a violated invariant in a particular object. That is,
the object is supposed to preserve rules about its internal state, and if you
can see the object, from outside, in an invalid intermediate state then the
invariant has been violated, from the standpoint of the client (this is not
to say that the object can never exist in the invalid intermediate state, just
that it should not be visible by the client in such a state). Thus, we want to
be able to detect that the invariant is violated, and also know what the
violation value is. To get both of these values from one method call, we
combine them in a tagging interface which only exists to provide a
meaningful name in the code: Feedback
//: c13:InvariantState.java
// Messenger carrying invariant data
public interface InvariantState {} ///:~
In this scheme, the information about success or failure is encoded in the
class name and type, to make the result more readable. The class
indicating success is: Feedback
//: c13:InvariantOK.java
// Indicates that the invariant test succeeded
public class InvariantOK implements InvariantState {} ///:~
To indicate failure, the InvariantFailure object will carry an object with
information about what caused the failure, typically so that it can be
displayed: Feedback
//: c13:InvariantFailure.java
// Indicates that the invariant test failed
public class InvariantFailure implements InvariantState {