Java Reference
In-Depth Information
public class Client {
public static void main(String[] args) {
System.out.println(Cache.getSum());
}
}
Solution 52: Sum Fun
On cursory inspection, you might think that this program adds the numbers from 1 to 100, but it
doesn't. Take a closer look at the loop. It is the typical half-open loop, so it goes from 0 to 99. With
that in mind, you might think that the program prints the sum of the numbers from 0 to 99. Using
the formula from the hint, this sum is 99 x 100 / 2, or 4,950. The program, however, thinks
otherwise. It prints
9900
, fully twice this value. What accounts for its enthusiasm?
The author of the program obviously went to a lot of trouble to make sure that
sum
was initialized
before use. The program combines lazy and eager initialization and even uses synchronization to
make sure that the cache works in the presence of multiple threads. It seems that this program has
all the bases covered, yet it doesn't work. What's the matter with it?
Like the program in
Puzzle 49
, this program suffers from a class initialization ordering problem. To
understand its behavior, let's trace its execution. Before it can invoke
Client.main
, the VM must
initialize the class
Client
. This initialization is so simple that it isn't worth talking about. The
Client.main
method invokes
Cache.getSum
. Before the
getSum
method can be executed, the VM
must initialize the class
Cache
.
Recall that class initialization executes static initializers in the order they appear in the source. The
Cache
class has two static initializers: the
static
block at the top of the class and the initialization
of the static field
initialized
. The block appears first. It invokes the method
initializeIfNecessary
, which tests the field
initialized
. Because no value has been assigned to
this field, it has the default
boolean
value of
false
. Similarly,
sum
has the default
int
value of
0
.
Therefore, the
initializeIfNecessary
method does what you'd expect, adding 4,950 to
sum
and
setting
initialized
to
true
.
After the static block executes, the static initializer for the
initialized
field sets it back to
false
,
completing the class initialization of
Cache
. Unfortunately,
sum
now contains the correct cached
value, but
initialized
contains
false
: The
Cache
class's two pieces of critical state are out of
sync.
The
main
method in the
Client
class then invokes
Cache.getSum
, which in turn invokes
initializeIfNecessary
. Because the
initialized
flag is
false
, the
initializeIfNecessary
method enters its loop, which adds another 4,950 to the value of
sum
, increasing its value to 9,900.
The
getSum
method returns this value, and the program prints it.
Search WWH ::
Custom Search