Q:

Unexpected thread behavior. Visibility

I have the following code: 
public static boolean turn = true;

public static void main(String[] args) {
    Runnable r1 = new Runnable() {
        public void run() {
            while (true) {
                while (turn) {
                    System.out.print("a");
                    turn = false;
                }
            }
        }
    };
    Runnable r2 = new Runnable() {
        public void run() {
            while (true) {
                while (!turn) {
                    System.out.print("b");
                    turn = true;
                }
            }
        }
    };
    Thread t1 = new Thread(r1);
    Thread t2 = new Thread(r2);
    t1.start();
    t2.start();
}

In class we've learned about "Visibility" problems that may occur when using un-synchronized code.
I understand that in order to save time, the compiler will decide the grab turn to the cache in the CPU for the loop, meaning that the thread will not be aware if the turn value was changed in the RAM because he doesn't check it.
From what I understand, I would expected the code to run like this:
T1 will see turn as true -> enter loop and print -> change turn to false -> gets stuck
T2 will think turn hasn't changed -> will get stuck
I would expect that if T1 will start before T2: only 'a' will be printed and both threads will run in an infinite loop without printing anything else
However, when I'm running the code sometimes I get a few "ababa...." before both threads will stuck.
What am I missing ?
EDIT:
The following code does what I expect it: the thread will run in a infinite loop:
public class Test extends Thread {
boolean keepRunning = true;

public void run() {
    long count = 0;
    while (keepRunning) {
        count++;
    }
    System.out.println("Thread terminated." + count);
}

public static void main(String[] args) throws InterruptedException {
    Test t = new Test();
    t.start();
    Thread.sleep(1000);
    t.keepRunning = false;
    System.out.println("keepRunning set to false.");
}

}
How are they different from each other ?

A:

When I run the code, sometimes I get a few "ababa...." before both threads will stuck.

I suspect that what is happening is that the behavior is changing when the code is JIT compiled.  Before JIT compilation the writes are visible because the interpreter is doing write-throughs.  After JIT compilation, either the cache flushes or the reads have been optimized away ... because the memory model allows this.

What am I missing ?

What you are missing is that you are expecting unspecified behavior to be consistent.  It doesn't have to be.  After all, it is unspecified!  (This is true, even if my proposed explanation above is incorrect.)

