Reputation: 963
The sample code is as follows
public class Lazy<T> implements Supplier<T> {
public Lazy(Supplier<T> supplier) {
this.supplier = Objects.requireNonNull(supplier);
}
Supplier<T> supplier;
T value;
@Override
public T get() {
if (supplier != null) {
synchronized (this) {
if (supplier != null) {
value = supplier.get();
supplier = null;
}
}
}
return value;
}
}
I am worried that if "supplier" is a constructor. "supplier=null" may be executed before the object is initialized. An error similar to "double checked locking broken" may happen.
"supplier.get()==null" may be true in this class. So I don't check if the value is null
If it is thread unsafe, Should I add "volatile" before the "supplier" field? If it is thread safe, why?
Upvotes: 2
Views: 199
Reputation: 120858
This is a little bit complicated, but read this. In short, without volatile, this is broken. The entire construct can be greatly simplified:
public class Lazy<T> implements Supplier<T> {
private final Supplier<T> supplier;
volatile boolean computed = false;
T value;
public Lazy(Supplier<T> supplier) {
this.supplier = Objects.requireNonNull(supplier);
}
@Override
public T get() {
if (!computed) {
synchronized (this) {
if (!computed) {
value = supplier.get();
computed = true;
}
}
}
return value;
}
}
Upvotes: 3
Reputation: 380
The problem is, in theory, you have no control over what supplier.get() will return. It could be null, it could be a different value every time, etc. For this reason, I claim this code is not thread safe. Note also that "supplier" will only ever be null if they do:
new Lazy(null)
otherwise it will never be null. You might as well throw an exception in the constructor in this case
public Lazy(Supplier<T> supplier) {
if (supplier == null) {
throw new IllegalArgumentException("'supplier' must not be null");
}
this.supplier = supplier;
}
I'm not sure what you are trying to achieve here. If you want to intialize "value" lazily, only once, you could do something like:
if (value == null) {
synchronize (this) {
// test again to ensure no other thread initialized as we acquired monitor
if (value == null) {
value = supplier.get();
}
}
}
return value;
Upvotes: 0