简体   繁体   中英

Number of instance when using static volatile vs static

I read this article which claim using static member with java 1.5 is thread safe. Does it also mean that there will be only one instance of the member or different threads can cache different instance?

For example:

public class MyClass {
 private static Foo foo = null;

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

If I have several threads that do:

String foo = (new MyClass()).getFoo();

I guess that all threads will get same "init" foo but how many times foo will be init? and what if I'll add volatile and define foo as "public static volatile Foo..." will it change anything?

I read this article which claim using static member with java 1.5 is thread safe.

To be more specific, the Java Memory Model FAQ says specifically that:

If a field is set in a static initializer , it is guaranteed to be made visible, correctly, to any thread that accesses that class

In the code you posted, your static field is not set in a static initializer. If it did the following then there would not be an thread safety issue:

private static Foo foo = new Foo();

However in most other cases with static fields thread safety is an issue:

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.

Your other questions:

I guess that all threads will get same "init" Foo

There is no guarantee about that. Two threads could call getFoo() at the same time and get different instances of Foo for sure.

but how many times foo will be init?

This is unknown and depends on how many threads are running, data publication, etc..

what if I'll add volatile and define foo as "public static volatile Foo..." will it change anything?

It will make sure that at least Foo will be properly constructed when it is assigned to the static field but it in no way protects you again the race condition that happens when multiple threads call getFoo() at the same time.

Typically, I will use a volatile static field pattern when the construction of Foo is cheap and I don't mind if one of the threads uses its instance of Foo and then it is garbage collected. I just want the majority of the rest of the operations to use the same instance.

Another pattern is to do something like the following:

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

This might create and then throw away a couple extra instances of Foo but at least all threads will use the same instance of Foo .

However, if there must be one and only one instance of Foo and you can't construct it in a static initializer (see above), then you are going to be forced to lock around its construction.

Each thread can see null and each thread can create it's it's own instance. Using volatile make this less likely, but it can still happen.

What you need to do is to use some synchronization or static initialisation (which is thread safe)

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

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

or

public class MyClass {
 private static Foo foo = null;

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

This is true for Java 5.0 and all the other versions as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM