In Java, when can one "get away" with not using synchronized on variables that are read/write for multiple concurrent threads?
I read about a couple of surprising concurrency bugs: double-checked-locking and hash-maps, and have always used synchronize by default in shared read/write cases, however, I started to wonder when it's OK to not.
For example, what sort of general rules can I use to decide when it's actually safe to omit synchronized
from methods like this:
T getFoo() {
if (this.foo == null) {
this.foo = createFoo() // createFoo is always thread safe.
}
return this.foo;
}
Where:
T
might be primitives or arbitrary objectscreateFoo
is always thread-safe and can be called multiple times, but is otherwise unspecified.getFoo()
can be "eventually consistent".Is it OK if T are primitives like int
? What about Integer
? What about simple objects, like String? Etc.
When is it safe to not synchronize read/write variables?
The tongue-in-cheek answer is Only when you fully understand the implications to the underlying hardware, the JVM, and your application. If possible, I'd still recommend this approach, which begins with a lot of reading and experiments.
In practice, you should be able to use some commonly used patterns to minimize the amount of synchronized
methods or blocks in your code, without understanding all the intricacies of said patterns. That shouldn't increase the risk for your application too much, because there are non-trivial details even around the usage of synchronized
, and if you understood all of these details, you would have asked a different question.
Still, your mileage may vary, especially if the form and implementation of said commonly used patterns is not blessed by your local concurrency guru.
Enough blabbering, give me some examples
java.util.concurrent
where possible.volatile
volatile
variables.It's never safe to share data between threads without a memory barrier.
The correctness of your getFoo()
depends on the declaration of the foo
field.
If getFoo()
is called by multiple threads, every thread might end up with a different instance of T
. Without a memory barrier, actions by one thread (like assignment to the field foo
) might never become visible to other threads—ever! In other words, without synchronizaton, consistency, "eventual" or otherwise, is not guaranteed.
If foo
is volatile
, that serves as a sufficient memory barrier, and then you just have the problem that multiple threads might get a different T
for a short period of time as they race to create instances.
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