I have a String
field that is initialized to null
but then accessed by potentially more than one thread. The value will be lazily initialized to an idempotently calculated value upon first access.
Does this field need to be volatile
to be thread safe?
Here's an example.
public class Foo {
private final String source;
private String BAR = null;
public Foo(String source) {
this.source = source;
}
private final String getBar() {
String bar = this.BAR;
if (bar == null) {
bar = calculateHashDigest(source); // e.g. an sha256 hash
this.BAR = bar;
}
return bar;
}
public static void main(String[] args) {
Foo foo = new Foo("Hello World!");
new Thread(() -> System.out.println(foo.getBar())).start();
new Thread(() -> System.out.println(foo.getBar())).start();
}
}
I used System.out.println()
for the example but I'm not worried about what happens when its calls are interlocked. (Though I'm pretty sure that's thread-safe too.)
Does BAR
need to be volatile
?
I'm thinking the answer is No, volatile
is not required, and Yes it's thread safe, primarily because of this excerpt from JLS 17.5:
final
fields also allow programmers to implement thread-safe immutable objects without synchronization. A thread-safe immutable object is seen as immutable by all threads, even if a data race is used to pass references to the immutable object between threads.
And since the char value[]
field of String
is indeed final
.
(int hash
isn't final
but it's lazy initialization looks sound as well.)
Edit: Edit to clarify the value intended for BAR
is a fixed value. Its calculation is idempotent and has no side-effects. I don't mind if the calculation is repeated across threads, or if BAR
becomes effectively a thread-local due to memory-caching / visibility. My concern is, if it's non-null then it's value is complete and not somehow partial.