我读过这篇文章,声称使用静态成员与java 1.5是线程安全的。
更具体地讲,在Java Memory Model FAQ特别说:
如果某个字段在静态初始化设置,可以保证它是可见的,正确的,以访问任何线程该类
在您发布的代码,你static
字段是静态初始化不集。如果做了以下那么就不会有一个线程安全问题:
private static Foo foo = new Foo();
然而,在其他大多数情况下与static
领域的线程安全性问题:
换句话说,不要将在另一个线程可能能够看到的地方引用正在构建的对象;请勿将其分配给静态字段,请勿将其注册为任何其他对象的侦听器,依此类推。
你的其他问题:
我想所有的线程会得到同样的 “初始化” 富
没有关于保证。两个线程可以同时调用getFoo()
,肯定会得到Foo
的不同实例。
但是foo会被初始化多少次?
这是未知的,取决于有多少线程在运行,数据发布等。
如果我将添加挥发性和foo定义为“公共静态挥发富......”它会改变什么吗?
这将确保至少Foo
会当它被分配到静态字段,但它绝不再保护你当多个线程在同一时间拨打getFoo()
这种情况发生的竞态条件妥善建造。
通常,当Foo
的构造便宜时,我将使用volatile static
字段模式,并且我不介意其中一个线程使用其实例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
实例,并且您不能在静态初始化程序中构建它(请参阅上文),那么您将被迫锁定其构造。
在'Foo'的构造函数中放入一个日志语句并尝试。 –