开发者

Java concurrency: is final field (initialized in constructor) thread-safe?

开发者 https://www.devze.com 2023-03-15 11:00 出处:网络
Can anyone tell me whether this class is threadsafe or not ? class Foo { private final Map<String,String> aMap;

Can anyone tell me whether this class is threadsafe or not ?

class Foo {

    private final Map<String,String> aMap;

    public Foo() {
        aMap = new HashMap<String, String>();
        aMap.put("1", "a");
        aMap.put("2", "b");
        aMap.put("3", "c");
    }

    public String get(String key开发者_JAVA技巧) {
        return aMap.get(key);
    }

}

Edit: It my fault to not clarify the question. According to JMM FAQ :

A new guarantee of initialization safety should be provided. If an object is properly constructed (which means that references to it do not escape during construction), then all threads which see a reference to that object will also see the values for its final fields that were set in the constructor, without the need for synchronization.

This made me confuse that the set to aMap is aMap = new HashMap<String, String>();. So other threads can see these

aMap.put("1", "a");
aMap.put("2", "b");
aMap.put("3", "c");

or not ?

Edit: I found this question that exactly closed to my question


As already pointed out it's absolutely thread-safe, and final is important here due to its memory visibility effects.

Presence of final guarantees that other threads would see values in the map after constructor finished without any external synchronization. Without final it cannot be guaranteed in all cases, and you would need to use safe publication idioms when making newly constructed object available to other threads, namely (from Java Concurrency in Practice):

  • Initializing an object reference from a static initializer;
  • Storing a reference to it into a volatile field or AtomicReference;
  • Storing a reference to it into a final field of a properly constructed object; or
  • Storing a reference to it into a field that is properly guarded by a lock.


Yes it is. There is no way to modify the reference aMap itself, or add to the map after the constructor (barring reflection).

If you expose aMap it will not be, because two threads could then modify the map at the same time.

You could improve your class by making aMap unmodifiable via Collections.unmodifiableCollection or Collections.unmodifiableMap.


Guava has immutable classes for making this sort of thing easier and guaranteed immutable:

private final ImmutableMap<String, String> aMap = ImmutableMap.of(
    "1", "a",
    "2", "b",
    "3", "c");


Yes it is, provided this is the entire class definition and not a snippet thereof.

The key fact is that there is no way the contents of aMap can be modified post-construction.


This class has no concurrency issue cause you expose only a get method. If you add some method that modify the map you have to mark this method as synchronized.


As it is now it should be thread-safe. However if you add other methods which modify the hashmap then no.


I don't think the above code snippet is thread safe. The only line that is code safe is

aMap = new HashMap<String, String>();

As per example given in http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html,

class FinalFieldExample {
    final int x;
    int y;
    static FinalFieldExample f;
    public FinalFieldExample() {
      x = 3;
      y = 4;
    }

    static void writer() {
       f = new FinalFieldExample();
    }

   static void reader() {
     if (f != null) {
       int i = f.x; // x is guaranteed to be 3
       int j = f.y; // y can have any value 
     }
   }
}

This means that once the final fields are initialized there is no thread safety guaranteed. Since only the reference assignment is guaranteed to be thread safe and object itself can be mutable as per your example. Following statement might not be thread safe

aMap.put("1", "a");
aMap.put("2", "b");
aMap.put("3", "c");

EDIT My bad saw the comments below the code later

The ability to see the correctly constructed value for the field is nice, but if the field itself is a reference, then you also want your code to see the up to date values for the object (or array) to which it points. If your field is a final field, this is also guaranteed. So, you can have a final pointer to an array and not have to worry about other threads seeing the correct values for the array reference, but incorrect values for the contents of the array. Again, by "correct" here, we mean "up to date as of the end of the object's constructor", not "the latest value available".


It's question asked long time ago. However, I have decided to answer the question. First of all, it is totally thread safe code. The reasons are the following.

  1. Its state is well-encapsulated. The class's state consists of map and its key value pairs which are strings which are immutable. So, through encapsulation the class shares its state with other threads in thread-state manner.

  2. Only public method Foo class provides get which publishes immutable objects which are safe for parallel threads because they can't modify published object.

  3. map variable is final which makes it immutable and provides memory visibility.

To answer the question, it's actually safe initialization. No objects are escaping. Other threads does not see.


As it is described in Java Concurrency in Practice in section 16.3, this has to be thread-safe.

Initialization safety guarantees that for properly constructed objects, all threads will see the correct values of final fields that were set by the constructor, regardless of how the object is published. Further, any variables that can be reached through a final field of a properly constructed object (such as the elements of a final array or the contents of a HashMap referenced by a final field) are also guaranteed to be visible to other threads.

For objects with final fields, initialization safety prohibits reordering any part of construction with the initial load of a reference to that object. All writes to final fields made by the constructor, as well as to any variables reachable through those fields, become “frozen” when the constructor completes, and any thread that obtains a reference to that object is guaranteed to see a value that is at least as up to date as the frozen value. Writes that initialize variables reachable through final fields are not reordered with operations following the post-construction freeze

And an example from that section:

 @ThreadSafe
 public class SafeStates {

    private final Map<String, String> states;

    public SafeStates() {
        states = new HashMap<String, String>(); states.put("alaska", "AK");
        states.put("alabama", "AL");
        ...
        states.put("wyoming", "WY");
     }

     public String getAbbreviation(String s) { 
        return states.get(s);
     } 
 }
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号