Search code examples
javamultithreadingvolatile

Java threads checking for change on non-volatile variable seems to take forever


I'm sorry if I'm doing a bad thing with this, but I have a question which is a spin-off of this question here:

Why volatile in java 5+ doesn't synchronize cached copies of variables with main memory?

Basically, I wanted to see what happends when the volatile is eliminated from the a variable. Here's the code from the original question, with my modification applied:

public class Test {
    //volatile static private int a;
    static private int a;
    static private int b;
public static void main(String [] args) throws Exception {
    for (int i = 0; i < 100; i++) {
        new Thread() {

            @Override
            public void run() {
                int tt = b; // makes the jvm cache the value of b

                while (a==0) {

                }
                //some threads never get here (past the a==0 loop)

                if (b == 0) {
                    System.out.println("error");
                }
            }

        }.start();
    }

    b = 1;
    a = 1;
}
}

And what happens on my laptop (Win 7 64, JVM build 1.7.0_04-b22) is that without the volatile, the code seems to run forever (left in running for 20 minutes). Adding some more console output told me that while most of the 100 threads do finally see the change in a from 0 to 1, there's always a few left (less than 10) that keep doing the a==0 loop.

My question is: will those threads finally see that change as well? If yes, is it normal to take 10s of thousands of times more time to do it as compared to the majority of similar threads? How come?


Solution

  • Each thread has its own local memory. Making changes, even to the same variable, from one thread, may not (and you should assume: will not) propagate to other threads.

    To enforce memory synchronization between threads you have to use synchronized block and obtain the variable value within that block.

    The simplest way to achieve that is to use Atomic-class variable, like AtomicInteger. It ensures atomic and data race-free access to their values.

    Using volatile is an option, but it prevents any compiler optimization of that variable. Besides, it only applies to atomic operations, you may encounter various race conditions with it, which can be avoided using a synchronized block.

    The most precise and detailed information on how Java Memory model actually works can be found in the Java Language Specification and JSR-133 Java Memory Model and Thread Specification. Various interpretations (including mine) of these documents may be wrong, so remember to look at reliable sources when in doubt.