Java 使用静态volatile与静态volatile时的实例数

Java 使用静态volatile与静态volatile时的实例数,java,multithreading,Java,Multithreading,我读了一篇文章,文章声称在Java1.5中使用静态成员是线程安全的。这是否也意味着只有一个成员实例,或者不同的线程可以缓存不同的实例 例如: public class MyClass { private static Foo foo = null; public String getFoo() { if (foo== null) foo= new Foo(); //init foo... } } 如果我有几个线程可以: String foo =

我读了一篇文章,文章声称在Java1.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定义为“publicstaticvolatilefoo…”会有什么变化吗?

每个线程都可以看到
null
,每个线程都可以创建自己的实例。使用
volatile
可以降低这种可能性,但这种情况仍然可能发生

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


Java 5.0和所有其他版本也是如此。

每个线程都可以看到
null
,每个线程都可以创建自己的实例。使用
volatile
可以降低这种可能性,但这种情况仍然可能发生

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


Java 5.0和所有其他版本也是如此。

每个线程都可以看到
null
,每个线程都可以创建自己的实例。使用
volatile
可以降低这种可能性,但这种情况仍然可能发生

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


Java 5.0和所有其他版本也是如此。

每个线程都可以看到
null
,每个线程都可以创建自己的实例。使用
volatile
可以降低这种可能性,但这种情况仍然可能发生

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

Java 5.0和所有其他版本也是如此

我读了这篇文章,文章声称在Java1.5中使用静态成员是线程安全的

更具体地说,报告明确指出:

如果在静态初始值设定项中设置了一个字段,则保证该字段对访问该类的任何线程都是正确可见的

在您发布的代码中,您的
静态
字段未在静态初始值设定项中设置。如果执行以下操作,则不会出现线程安全问题:

private static Foo foo = new Foo();
但是,在大多数其他情况下,对于
静态
字段,线程安全是一个问题:

换句话说,不要将对正在构造的对象的引用放在其他线程可能看到它的任何地方;不要将其分配给静态字段,不要将其注册为任何其他对象的侦听器,以此类推

你的其他问题:

我猜所有线程都会得到相同的“init”Foo

这不能保证。两个线程可以同时调用
getFoo()

但是foo会被初始化多少次呢

这是未知的,取决于正在运行的线程数、数据发布等

如果我添加volatile并将foo定义为“publicstaticvolatilefoo…”会有什么变化吗

当它被分配到静态字段时,它将确保至少
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();
   }
}
private static final AtomicReference fooRef=new AtomicReference();
公共静态字符串getFoo(){
Foo-Foo=fooRef.get();
如果(foo!=null){
返回foo;
}
foo=新的foo();
if(fooRef.compareAndSet(null,foo)){
返回foo;
}否则{
//foo ref由另一个线程设置,因此不使用我们的foo
返回fooRef.get();
}
}
这可能会创建并丢弃两个额外的
Foo
实例,但至少所有线程都将使用相同的
Foo
实例

但是,如果必须有且只有一个
Foo
实例,并且您不能在静态初始值设定项中构造它(参见上文),那么您将被迫锁定其构造

我读了这篇文章,文章声称在Java1.5中使用静态成员是线程安全的

更具体地说,报告明确指出:

如果在静态初始值设定项中设置了一个字段,则保证该字段对访问该类的任何线程都是正确可见的

在您发布的代码中,您的
静态
字段未在静态初始值设定项中设置。如果执行以下操作,则不会出现线程安全问题:

private static Foo foo = new Foo();
但是,在大多数其他情况下,对于
静态
字段,线程安全是一个问题:

换句话说,不要将对正在构造的对象的引用放在其他线程可能看到它的任何地方;不要将其分配给静态字段,不要将其注册为任何其他对象的侦听器,以此类推

你的其他问题:

我猜所有线程都会得到相同的“init”Foo

这不能保证。两个线程可以同时调用
getFoo()

但是foo会被初始化多少次呢

这是未知的,取决于正在运行的线程数、数据发布等

如果我添加volatile并将foo定义为“publicstaticvolatilefoo…”会有什么变化吗

会的
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();
   }
}