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:
Could anyone either suggest how I could design such an experiment or explain why my idea is flawed?
Many thanks.
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.
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).
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.
Put the assignment in a synchronized block.
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");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With