问题
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
instancebevolatile. This forces a write memory barrier to be crossed wheninstanceis 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
AtomicReferencebut that just wraps avolatileobject so makinginstancevolatileis similar.You could make sure that all of the fields in
DataClientarefinalorvolatileso 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