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

In Java, when can I “leave” without using synchronized variables that are read / written for multiple parallel threads?

I read about a few amazing concurrency errors: double-checked-locking and hash cards and always used default synchronization in general read / write cases, however I started wondering when is it normal that there isn’t.

For example, what general rules can I use to decide when it is really safe to omit synchronized from such methods:

 T getFoo() { if (this.foo == null) { this.foo = createFoo() // createFoo is always thread safe. } return this.foo; } 

Where:

  • T can be primitives or arbitrary objects
  • createFoo always thread safe and can be called several times, but otherwise not specified.
  • getFoo() may be "ultimately sequential."

Well, if T are primitives of type int ? What about Integer ? What about simple objects like String? Etc.

+5
source share
2 answers

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

The answer to the lesson is only when you fully understand the implications for the underlying hardware, the JVM, and your application. If possible, I still recommend this approach, which starts with a lot of reading and experimenting.

In practice, you should use some commonly used patterns to minimize the number of methods or synchronized blocks in your code, without understanding all the intricacies of these patterns. This should not increase the risk for your application too much, because there are non-trivial details even around using synchronized , and if you understood all these details, you would ask another question.

However, your mileage may vary, especially if the form and implementation of the commonly used patterns mentioned are not blessed by your local concurrency guru.

Enough to clarify, give some examples

  • Using stream collections, utilities, and types.
    • This includes, when possible, java.util.concurrent .
    • There is also a wonderful amount of additional libraries and tools for Java outside of the JDK and its ecosystem, but they are not so easily qualified as "apply now, read later."
  • Using volatile
  • Using secure provisioning and secure publishing
  • Saving data streams locally.
    • A view of hassle-free and surprisingly often applicable.
+2
source

You cannot safely exchange 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, each thread may have a different instance of T Without a memory barrier, the actions of one thread (for example, assigning the field foo ) will never become visible to other threads! In other words, without synchronization, consistency, "ultimately" or otherwise, is not guaranteed.

If foo is volatile , this serves as a sufficient memory barrier, and then you just have a problem that several threads can get different T for a short period of time when they are involved in creating instances.

+1
source

All Articles