我有一个String
字段,初始化为null
但后来可能被多个线程访问。在首次访问时,该值将被懒惰地初始化为幂等计算值。
这个字段是否必须是volatile
才能是线程安全的?
这是一个例子。
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();
}
}
我使用System.out.println()
作为示例,但我并不担心当它的调用是互锁时会发生什么。 (虽然我很确定这也是线程安全的。)
BAR
需要是volatile
吗?
我认为答案是否定的,volatile
不是必需的,是的,它是线程安全的,主要是因为这个excerpt from JLS 17.5:
final
字段还允许程序员在没有同步的情况下实现线程安全的不可变对象。线程安全的不可变对象被所有线程视为不可变的,即使使用数据争用传递线程之间的不可变对象的引用也是如此。
而且因为char value[]
的String
领域确实是final
。
(int hash
不是final
,但它的懒惰初始化看起来也很合适。)
编辑:编辑以阐明用于BAR
的值是固定值。它的计算是幂等的,没有副作用。我不介意是否跨线程重复计算,或者由于内存缓存/可见性,BAR
实际上变为线程本地。我担心的是,如果它是非空的,那么它的值是完整的而不是某种程度上是偏的。