问题
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
bevolatile
. This forces a write memory barrier to be crossed wheninstance
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 avolatile
object so makinginstance
volatile
is similar.You could make sure that all of the fields in
DataClient
arefinal
orvolatile
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