Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When is it safe to not synchronize read/write variables?

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 objects
  • createFoo 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.

like image 598
Richard Levasseur Avatar asked Oct 18 '22 09:10

Richard Levasseur


2 Answers

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

  1. Using thread-safe collections, utilities, and types.
    • This includes relying on java.util.concurrent where possible.
    • There's also a wonderful amount of additional libraries and tools for Java outside of the JDK and its ecosystem, but they do not qualify as easily for "apply now, read more later".
  2. Using volatile
    • You can take a look at http://www.ibm.com/developerworks/library/j-jtp06197/ for some common patterns using volatile variables.
  3. Using safe initialization and safe publication
    • You can take a look at http://shipilev.net/blog/2014/safe-public-construction/ for what can be a slightly more advanced explanation than is appropriate for a beginner.
    • I'd also put using immutable types here.
  4. Keeping data thread-local.
    • Kind of a no-brainer, and surprisingly often applicable.
like image 115
Dimitar Dimitrov Avatar answered Oct 21 '22 04:10

Dimitar Dimitrov


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.

like image 34
erickson Avatar answered Oct 21 '22 04:10

erickson