You won't be able to easily observe the effects of a lack of barriers in your code on an x86 because it has a fairly strong memory model. But that does not mean that the same code would not break on a different architecture. On x86, you generally need to play with the JIT compiler and help it make an optimisation that would not be allowed with a volatile variable, for example variable hoisting.
The code below, on my machine with hotspot 7u25 server, never ends if the variable is non-volatile but stops promptly if it is. You might need to change the sleep delay depending on your machine.
public class Test {
static /* volatile */ boolean done = false;
public static void main(String[] args) throws Exception {
Runnable waiter = new Runnable() {
@Override public void run() {
while(!done);
System.out.println("Exited loop");
}
};
new Thread(waiter).start();
Thread.sleep(100); //wait for JIT compilation
done = true;
System.out.println("done is true");
}
}