Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Under what conditions will writes to non-volatile variables be unseen by other threads? Can I force such conditions for experimental purposes?

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.

like image 878
devrobf Avatar asked Aug 28 '13 15:08

devrobf


People also ask

How volatile will work if we have multiple threads writing to a volatile variable?

If you write volatile variable from multiple threads without using any synchronized constructs, you are bound to get data inconsistency errors. Use volatile variables without synchronization in case of single write thread and multiple read threads for atomic operations.

What will happen if the switches are not declared as a volatile?

Failing to declare the variable as volatile will result in the compiler optimizing the code in such a way that it will read the port only once and keep using the same value in a temporary register to speed up the program (speed optimization).

Can a volatile variable be read and write?

You can use a volatile variable if you want to read and write long and double variable automatically. It can be used as an alternative way of achieving synchronization in Java. All reader threads will see the updated value of the volatile variable after completing the write operation.

How can we ensure that all the threads read the updated value of any variable?

Put the assignment in a synchronized block.


1 Answers

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");
    }
}
like image 77
assylias Avatar answered Sep 22 '22 02:09

assylias