我读到this文章声称在 java 1.5 中使用静态成员是线程安全的。这是否也意味着该成员只有一个实例,或者不同的线程可以缓存不同的实例?
例如:
public class MyClass {
private static Foo foo = null;
public String getFoo()
{
if (foo== null)
foo= new Foo();
//init foo...
}
}
如果我有几个线程这样做:
String foo = (new MyClass()).getFoo();
我猜所有线程都会得到相同的“init”foo,但是foo会被init多少次?如果我添加 volatile 并将 foo 定义为“public static volatile Foo...”,它会改变什么吗?
最佳答案
I read this article which claim using static member with java 1.5 is thread safe.
更具体地说,Java Memory Model FAQ具体说:
If a field is set in a static initializer, it is guaranteed to be made visible, correctly, to any thread that accesses that class
在您发布的代码中,您的 static
字段未在静态初始值设定项中设置。如果它执行以下操作,则不会出现线程安全问题:
private static Foo foo = new Foo();
但是,在大多数其他情况下,使用 static
字段时,线程安全是一个问题:
In other words, do not place a reference to the object being constructed anywhere where another thread might be able to see it; do not assign it to a static field, do not register it as a listener with any other object, and so on.
您的其他问题:
I guess that all threads will get same "init" Foo
对此没有任何保证。两个线程可以同时调用 getFoo()
并肯定会获取 Foo
的不同实例。
but how many times foo will be init?
这是未知的,取决于正在运行的线程数、数据发布等。
what if I'll add volatile and define foo as "public static volatile Foo..." will it change anything?
它将确保至少 Foo
在分配给静态字段时能够正确构造,但它绝不会再次保护您在多个线程调用 时发生的竞争条件同时调用 getFoo()
。
通常,当 Foo
的构造很便宜并且我不介意其中一个线程使用其 实例时,我会使用
然后它被垃圾收集。我只是希望其余大部分操作使用同一个实例。 volatile 静态
字段模式code>Foo
另一种模式是执行如下操作:
private static final AtomicReference<Foo> fooRef = new AtomicReference<Foo>();
public static String getFoo() {
Foo foo = fooRef.get();
if (foo != null) {
return foo;
}
foo = new Foo();
if (fooRef.compareAndSet(null, foo)) {
return foo;
} else {
// foo ref was set by another thread so our Foo is not used
return fooRef.get();
}
}
这可能会创建然后丢弃几个额外的 Foo
实例,但至少所有线程都会使用相同的 Foo
实例。
但是,如果必须有且仅有一个 Foo
实例,并且您无法在静态初始值设定项中构造它(参见上文),那么您将被迫锁定其实例施工。
关于java - 使用 static volatile 与 static 时的实例数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22213087/