Should I synchronize a static mutable variable?

There are several questions on this issue, but most skirts are around this problem, because this is not the purpose of the issue.

If I have static volatile in my class:

private static volatile MyObj obj = null; 

and in the following method below:

 public MyObj getMyObj() { if (obj == null) { obj = new MyObj();// costly initialisation } return obj; } 

will I need to synchronize to ensure that only one thread will write to the field or will any records be immediately visible to other threads evaluating conditional obj == null ?

In other words: is it unstable for you to force synchronization of write access in a static variable?

+7
source share
5 answers

You will definitely need some kind of lock to ensure that only one stream is written to the field. Regardless of volatility, two threads can “see” that obj is null, and then both start initializing with your current code.

Personally, I would take one of three options:

  • Initialize the loading of the class (knowing that it will be lazy, but not as lazy as it would until getMyObj is called):

     private static final MyObj obj = new MyObj(); 
  • Use unconditional lock:

     private static MyObj obj; private static final Object objLock = new Object(); public static MyObj getMyObj() { synchronized(objLock) { if (obj == null) { obj = new MyObj(); } return obj; } } 
  • Use a nested class for laziness this way:

     public static MyObj getMyObj() { return MyObjHolder.obj; } private static class MyObjHolder { static final MyObj obj = new MyObj(); } 
+8
source

Yes, you should absolutely synchronize (or use a better idiom such as the Singleton Holder idiom ). Otherwise, you risk multiple threads initializing your object several times (and then subsequently using different instances).

Consider a sequence of events like this:

  • Thread A enters getMyObj() and sees that obj == null
  • Thread B enters getMyObj() and sees that obj == null
  • Thread A builds a new MyObj() - let him call it objA
  • Thread B creates a new MyObj() - let him call it objB
  • Topic A designates objA - obj
  • Thread B assigns objB to obj (which is not null anymore at this point, so the link to objA assigned by thread A is overwritten)
  • Topic A exits getMyObj() and starts using objA
  • Thread B terminates getMyObj() and starts using objB

This scenario can happen with any number of threads. Please note that although I have adopted a strict order of events for simplicity, in a real multi-threaded environment, events 1-2, 3-4 and / or 7-8 can partially or completely overlap in time without changing the end of the result.

Example to holder idiom:

 public class Something { private Something() { } private static class LazyHolder { public static final Something INSTANCE = new Something(); } public static Something getInstance() { return LazyHolder.INSTANCE; } } 

This is guaranteed since INSTANCE is final . The Java memory model ensures that final fields are initialized and displayed correctly for any number of threads when loading the containing class. Since LazyHolder private and only refers to getInstance() , it will only load the first time getInstance() called. And at this point, INSTANCE initialized in the background and the JVM is safely published.

+3
source

No, you still have to synchronize access. volatile allows changes made to a variable by one thread, visible to other threads.

Imagine the following thread of execution (assuming two threads T1 and T2):

  • obj is null initally.
  • T1: if (obj == null): YES
  • T2: if (obj == null): YES
  • T1: creates a new instance of MyObj and assigns it to obj
  • T2: also creates a new instance of MyObj and assigns it to obj

You create an object twice, which, as you expected, will be created only once. And this is not the worst case scenario. You could return an object that is no longer bound to your variable.

+1
source

This code is not thread safe. If multiple threads perform a function, then multiple instances of MyObj can be created. Here you need some form of synchronization.

The main problem is that this block code:

 if (obj == null) { obj = new MyObj();// costly initialisation } 

is not atomic. In fact, this is a very long way from the atomic one.

0
source

Another way to handle this is to double check (NOTE: only works with Java 5 or later, see here ):

 public class DoubleCheckingSingletonExample { private static final Object lockObj = new Object(); private static volatile DoubleCheckingSingletonExample instance; private DoubleCheckingSingletonExample() { //Initialization } public static DoubleCheckingSingletonExample getInstance() { if(instance == null) { synchronized(lockObj) { if(instance == null) { instance = new DoubleCheckingSingletonExample(); } } } return instance; } } 

When getInstance is called from two threads simultaneously, both will first see the instance as null, the other enters the synchronized block and instantiates the object. Another will see that the instance is no longer null and will not try to create it. Further calls to getInstance will see that the instance is not null and will not try to block at all.

0
source

All Articles