Saturday, June 9, 2007

Weekend Multithreaded Puzzler Fun!

Although I didn't go this year, one of my favorite parts of JavaOne is always Josh Bloch and Neal Gafter's talk on Java Puzzlers. (This year, Bill Pugh, my graduate advisor, stepped in for Neal.) A puzzler is a short snippet of code which has unexpected results, usually because some language or API feature behaves in a strange way. I enjoy them because I always think it is truly wonderful to have the depth of my ignorance exposed.

Josh and Neal wrote an excellent book with all of the Java Puzzlers through 2005, which is highly recommended, and occupies a place of honor in the stack of books in my bathroom.

The point of all of this is that occasionally, I will send Josh a multithreaded puzzler, and he will tell me it is no good, because you can't reproduce it every time. Here's one I sent him a couple of months ago.

It turns out that the following snippet of code displays the odd behavior 100% of the time under the JVM on my MacBook Pro (JDK 1.5.0_07), and won't display it at all on Linux. I haven't tried Windows. Can you figure out what the odd behavior will be? If you have an Intel-based Mac, you can probably even reproduce it.


class A {
volatile static int x = 1;
static {
B.y = 1;
}
}

class B {
volatile static int y = 2;
static {
A.x = 2;
}
}

public class Test {
public static void main(String [] args) {
Thread t1, t2;
(t1 = new Thread() {
public void run() {
A.x = 1;
}
}).start();
(t2 = new Thread () {
public void run() {
B.y = 2;
}
}).start();
try {
t1.join(); t2.join();
} catch (InterruptedException e) {}
}

}

3 comments:

Danny said...

I have jdk 1.6/windows, so I ran this code with a grain of salt. I got A.x=1 & B.y=2 (by adding prints after the joins), which I assume is not the odd behavior.

I'm guessing the odd behavior is A.x=2 and B.y=1. (Am I completely off?) The only way I can imagine this happening is that:

1. the static variables are initialized.
2. The threads are run.
3. The static blocks are reordered such that they set the final values.

Maybe this was allowed by your compiler/jmm because it knew that setting B.y doesn't effect x (and setting A.x doesn't effect y), so it found a way to optimize by reordering the actions?

Danny said...

Haha, you posted the answer. Ok, well I tried.

Unknown said...

@Danny - I disagree with Danny here. I think reordering if atall it happens, will happens with respect to A.x or B.y but since both x and y are volatile, reordering should be taken care of. The only possible thing is the atomicity so in worst case the answer would either be A.x = 2 or B.y = 1.