Question

I used to think there are only two multithreading hazards:

  1. Race condition: a thread reads x, and before it can write back another thread write x.
  2. Unstable state: a thread writes x and y, but between those two writes another thread reads x's new value and y's old value even though those variables should change together.

I figured that if I can reason these things can't happen, I don't need to synchronize. But from an argument over the D language forums I learned there can be other hazards from how the compiler, the CPU and the memory work behind the scenes:

  1. Both compiler and CPU can change the order of instruction in a way that does not affect single-threading execution but can break my reasoning about non-synchronized thread safety. For example, an object can be allocated and assigned to the thread-shared variable and only then the constructor will be called. It shouldn't matter in single-threading, but with multithreading another thread can now access the uninitialized object.
  2. Caching means that even if the order of operations is correct, the synching between the global memory and the local caches both threads use can mess with the order one thread can see another thread's changes.
  3. Unaligned pointer-sized writes are not atomic, and in some architectures even aligned ones might not be atomic, so the pointer itself might be corrupted(=in unstable state) when the other thread reads it, because the thread that written it only written half it's bytes.

I now work on an Android project and once again I face a similar problem - I can reason about some functions being safe from race conditions and unstable states, but not about those three behind-the-scenes hazards.

I've encountered the Java volatile member fields, and from what I gather it uses memory barriers to prevent the CPU from messing with order of execution(and obviously the compiler won't mess with it if it prevents the CPU from doing so) so it should solve the first hazard. But what about the other two?

  • Does it solve the caching problem? Isn't the caching under the responsibility of the OS? Does it signal the OS, forcing it to refresh the cache?
  • Does it solve the non-atomic-writes problem? Does it force variable alignment and uses the JVM's runtime knowledge to use synchronization when even aligned pointer-sized writes are not atomic?

In other words - if I can reason that race conditions and unstable states are not a problem, and mark the shared variable as volatile, is it safe to not use synchronization?

Was it helpful?

Solution

I don't know about the first and the third issue (some link about the first one would be nice if you're sure about it) but I'm sure it fixes your second issue because volatile ensures the flush of the local cache.

See this or this about this topic.

Update

I think I found the perfect link for your question:

http://javarevisited.blogspot.com/2011/06/volatile-keyword-java-example-tutorial.html#ixzz2vjymNleY

1. About reordering

By the way use of volatile keyword also prevents compiler or JVM from reordering of code or moving away them from synchronization barrier.

2. About caching

Volatile keyword in Java is used as an indicator to Java compiler and Thread that do not cache value of this variable and always read it from main memory

3. About atomic read and write

In Java reads and writes are atomic for all variables declared using Java volatile keyword (including long and double variables).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top