2014-03-06 55 views
0

我读取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(); 

我想所有的线程会得到同样的“初始化”富不过多少次foo将被初始化?以及如果我将添加volatile并将foo定义为“public static volatile Foo ...”会改变什么?

+0

在'Foo'的构造函数中放入一个日志语句并尝试。 –

回答

1

每个线程都可以看到null,每个线程都可以创建它自己的实例。使用volatile使这个可能性更小,但它仍然可以发生。

你需要做的是使用一些同步或静态初始化(这是线程安全的)

public class MyClass { 
private static final Foo foo = new Foo(); 

public static String getFoo() { return foo; } 
} 

public class MyClass { 
private static Foo foo = null; 

public static synchronized String getFoo() 
{ 
    if (foo== null) 
     foo= new Foo(); 
    //init foo... 
    } 
} 

这是Java 5.0和所有其他版本为真好。

+0

你的锁的粒度超过需要 - 这就是为什么双重检查锁通常用于这种情况。 – dcastro

+1

@dcastro在99%的情况下,双重检查锁定比您需要的要复杂得多,因此几乎总是有一种更好的方法来避免这样做。 –

2

我读过这篇文章,声称使用静态成员与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实例,并且您不能在静态初始化程序中构建它(请参阅上文),那么您将被迫锁定其构造。