How to write getInstance method thread safe?

半城伤御伤魂 提交于 2019-12-13 09:09:36

问题


I have a getInstance method in my library which is being used in multiple threads but I am not sure whether it is thread safe:

protected static DataClient instance;

protected DataClient() {
    // do stuff here
}

public static synchronized void initialize() {
    if (instance == null) {
        instance = new DataClient();
    }
}

public static DataClient getInstance() {
    if (instance == null) {
        initialize();
    }
    return instance;
}

And this is the way I am using it:

DataClient.getInstance();

Is this thread safe and if not then can anyone explain why it is not thread safe?


回答1:


Is this thread safe and if not then can anyone explain why it is not thread safe?

It is not thread safe because you are in effect doing the double check locking error. There is nothing to protect the instance from being published before it is fully instantiated. Just because initialize() is synchronized doesn't mean that it won't publish instance before the end of the synchronized block which can be seen in the non-synchronized getInstance() method. So a thread calling getInstance() might get a reference to a partially initialized DataClient instance.

In additional, even if the creating thread publishes it, there is nothing that guarantees that another thread will update the memory associated with the object. Cached memory can cause partial objects and other critical memory issues.

Here are some ways to make it safe:

  • Easiest way is to make instance be volatile. This forces a write memory barrier to be crossed when instance is written and a read memory barrier to be crossed when it is accessed. That fixes the double-check-locking bug.
  • You can instantiate the object upon class load which is done synchronously:

    protected static final DataClient instance = new DataClient();
    
  • You could use an AtomicReference but that just wraps a volatile object so making instance volatile is similar.

  • You could make sure that all of the fields in DataClient are final or volatile so that the object would be fully constructed when the constructor finishes. Although still a bad pattern, I believe that this stops the memory model from being able to reorder constructor initialization after the object is constructed. For more read this page.

For example, let's say you have 2 threads. Thread1 calls getInstance() with instance being null. It calls initialize() which constructs the DataClient and publishes it to the static instance variable. It then reaches the end of the synchronized block so instance is published to central memory.

Thread2 now calls getInstance() and gets a reference to instance but has some of the parts of instance memory cached which is now out of date. Because it does not cross a read memory barrier, there is no mechanism for Thread2 to be forced to update its memory cache to ensure that instance has been properly shared. It might see instance with only some or none of the fields updated. Memory synchronization has to be applied by both the publisher and the consumer threads otherwise memory race conditions can occur.





回答2:


It should be, isn't the general idea of having something be "thread safe" also be guaranteed of Race Conditions? If so, I don't think this would be an issue when calling it that it could mess up your sequencing for other code.

EDIT: Basically, since you are only reading the instance, it shouldn't be an issue as you aren't also doing other actions to the instance in side of this method.



来源:https://stackoverflow.com/questions/34184749/how-to-write-getinstance-method-thread-safe

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!