When sharing an object’s state between different threads, other issues besides atomicity come into play. One of them is visibility.
The key fact is that without synchronization, instructions are not guaranteed to be executed in the order in which they appear in your source code. This won’t affect the result in a single-threaded program but, in a multi-threaded program, it is possible that if one thread updates a value, another thread doesn’t see the update when it needs it or doesn’t see it at all.
In a multi-threaded environment, it is the program’s responsibility to identify when data is shared between different threads and act in consequence (using synchronization).
The example in NoVisibility consists in two threads that share a flag. The writer thread updates the flag and the reader thread waits until the flag is set:
This program might result in an infinite loop, since the reader thread may not see the updated flag and wait forever.
With synchronization we can guarantee that this reordering doesn’t take place, avoiding the infinite loop. To ensure visibility we have two options:
- Locking: Guarantees visibility and atomicity (as long as it uses the same lock).
- Volatile field: Guarantees visibility.
The volatile keyword acts like some sort of synchronized block. Each time the field is accessed, it will be like entering a synchronized block. The main difference is that it doesn’t use locks. For this reason, it may be suitable for examples like the above one (updating a shared flag) but not when using compound actions.
We will now modify the previous example by adding the volatile keyword to the ready field.
Visibility will not result in an infinite loop anymore. Updates made by the writer thread will be visible to the reader thread:
Writer thread - Changing flag...
Reader Thread - Flag change received. Finishing thread.
We learned about another risk when sharing data in multi-threaded programs. For a simple example like the one shown here, we can simply use a volatile field. Other situations will require us to use atomic variables or locking.
This post is part of the Java Concurrency Tutorial series. Check here to read the rest of the tutorial.
You can take a look at the source code at github.
I'm publishing my new posts on Google plus and Twitter. Follow me if you want to be updated with new content.
Labels: Concurrency, Core, Java