Monday, January 25, 2010

Puzzle 55: Creationism











 < Day Day Up > 







Puzzle 55: Creationism



Sometimes, it is useful for a class to keep track of how many instances have been created. This is typically done by having its constructors increment a private static field. In the program that follows, the Creature class demonstrates this technique, and the Creator class exercises it, printing the number of Creature instances it has created. What does the program print?





public class Creator {

public static void main(String[] args) {

for (int i = 0; i < 100; i++)

Creature creature = new Creature();

System.out.println(Creature.numCreated());

}

}



class Creature {

private static long numCreated = 0;



public Creature() {

numCreated++;

}



public static long numCreated() {

return numCreated;

}

}














Solution 55: Creationism



This is a trick question. The program looks as though it ought to print 100, but it doesn't print anything, because it doesn't compile. If you tried to compile it, you may have found the compiler diagnostics to be less than helpful. This is what javac prints:





Creator.java:4: not a statement

Creature creature = new Creature();

^



Creator.java:4: ';' expected

Creature creature = new Creature();

^




A local variable declaration looks like a statement but technically speaking is not; it is a local variable declaration statement [JLS 14.4]. The syntax of the language does not allow a local variable declaration statement as the statement repeated by a for, while, or do loop [JLS 14.12-14]. A local variable declaration can appear only as a statement directly within a block. (A block is a pair of curly braces and the statements and declarations contained within it.)



There are two ways to fix the problem. The obvious way is to place the declaration in a block:





for (int i = 0; i < 100; i++) {

Creature creature = new Creature();

}




Note, however, that the program is not using the local variable creature. Therefore, it makes more sense to replace the declaration with a naked constructor invocation, emphasizing that the reference to the newly created object is being discarded:





for (int i = 0; i < 100; i++)

new Creature();




If either of these changes is made, the program will print 100 as expected.



Note that the variable used to keep track of the number of Creature instances (numCreated) is a long rather than an int. It is quite conceivable that a program might create more instances of some class than the maximum int value but not the maximum long value. The maximum int value is 231 - 1, or about 2.1 x 109; the maximum long value is 263 - 1, or about 9.2 x 1018. Today, it is possible to create about 108 objects per second, which means that a program would have to run about three thousand years before a long object counter would overflow. Even in the face of increasing hardware speeds, long object counters should be adequate for the foreseeable future.



Also note that the creation counting strategy in this puzzle is not thread-safe. If multiple threads can create objects in parallel, the code to increment the counter and the code to read it must be synchronized:





// Thread-safe creation counter

class Creature {

private static long numCreated;



public Creature() {

synchronized (Creature.class) {

numCreated++;

}

}



public static synchronized long numCreated() {

return numCreated;

}

}




Alternatively, if you are using release 5.0 or a later release, you can use an AtomicLong instance, which obviates the need for synchronization in the face of concurrency.





// Thread-safe creation counter using AtomicLong;

import java.util.concurrent.atomic.AtomicLong;



class Creature {

private static AtomicLong numCreated = new AtomicLong();



public Creature() {

numCreated.incrementAndGet();

}



public static long numCreated() {

return numCreated.get();

}

}




Note that it is not sufficient to declare numCreated to be volatile. The volatile modifier guarantees that other threads will see the most recent value assigned to a field, but it does not make the increment operation atomic.



In summary, a local variable declaration cannot be used as the repeated statement in a for, while, or do loop but can appear only as a statement directly within a block. Also, when using a variable to count instance creations, use a long rather than an int, to prevent overflow. Finally, if you are going to create instances in multiple threads, either synchronize access to the instance counter or use an AtomicLong.

















     < Day Day Up > 



    No comments: