I've recently been reading a lot here on SO and elsewhere about threaded memory management, in particular, the use of the volatile keyword. I'm beginning to feel reasonably confident with the concept, however, in order to full appreciate the effect it has I would like to try and run some experiments which illustrate it.

Here is my setup: I have a producer thread (it reads audio data from the microphone, related to my previous question, but the actual data doesn't matter) which passes on data as byte[] to a separate consumer thread. The way in which the data is shared between threads is the primary variable in my experiment: I have tried an ArrayBlockingQueue; I have tried a shared volatile byte[] reference (with a array = array self-reference as recommended in this blog post); and I have also tried a normal non-volatile byte[] with no self reference. Both threads also write the data to disk as they go along.

My hope was to find that, after running for some length of time, the non-volatile byte[] version would have discrepancies between the data that the producer attempted to share and the data that the consumer read data due to some memory writes not being visible in time, while the other two versions would have exactly the same data logged by each thread because of the precautions taken to ensure publication of memory writes. As it happens however, I find 100% accuracy whatever method I use.

I can already think of a few possibilities as to why this occurred, but my main question is: under what conditions are writes to a non-volatile variable unseen to another thread, which as far as I understand is the whole point of volatile? And can I force these conditions for experimental purposes?

My thoughts so far are:

  • Maybe the two threads are running on the same core and share the same cache, so memory writes are visible immediately?
  • Maybe CPU load is a factor? Perhaps I need many threads all doing different things before I see any problem?
  • Maybe I need to wait longer: perhaps such problems are very rare?

Could anyone either suggest how I could design such an experiment or explain why my idea is flawed?

Many thanks.

1

There are 1 answers

6
assylias On BEST ANSWER

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");
    }
}