Are final static variables thread safe in Java?

33,488

Solution 1

Initialization of static final fields in a static initialization block is thread safe. However, remember that the object to which a static final reference points may not be thread safe. If the object to which you refer is thread safe (e.g., it's immutable), you're in the clear.

Each individual HashMap contained in your outer HashMap is not guaranteed to be thread safe unless you use ConcurrentHashMap as suggested in your question. If you do not use a thread-safe inner HashMap implementation, you may get unintended results when two threads access the same inner HashMap. Keep in mind that only some operations on ConcurrentHashMap are synchronized. For example, iteration is not thread-safe.

Solution 2

Yes, this is thread safe too. All final members of your static class will be initialized before any thread is allowed to access them.

If the static block fails during initialization, an ExceptionInInitializerError will be raised in the thread that first attempts initialization. Subsequent attempt to reference the class will raise a NoClassDefFoundError.

In general, the contents of a HashMap have no guarantee of visibility across threads. However, the class initialization code uses a synchronized block to prevent multiple threads from initializing the class. This synchronization will flush the state of the map (and the HashMap instances that it contains) so that they will be correctly visible to all threads—assuming that no changes are made to the map, or the maps it contains, outside the class initializer.

See the Java Language Specification, §12.4.2 for information about class initialization and the requirement for synchronization.

Solution 3

What is thread-safe? Sure, the initialization of the HashMap is thread-safe in the respect that all Foo's share the same Map instance, and that the Map is guaranteed to be there unless an exception occurs in the static init.

But modifying the contents of the Map is most assuredly not thread safe. Static final means that the Map sharedData can not be switched for another Map. But the contents of the Map is a different question. If a given key is used more than once at the same time you may get concurrency issues.

Solution 4

No. Except if they are immutable.

The only thing they do is

  • Be class level accesible
  • Avoid the reference to be changed.

Still if your attribute is mutable then it is not thread safe.

See also: Do we synchronize instances variables which are final?

It is exactly the same except they are class level.

Solution 5

There is nothing inherently thread safe about a final static variable. Declaring a member variable final static only ensures that this variable is assigned to just once.

The question of thread safety has less to do with how you declare the variables but instead relies on how you interact with the variables. So, it's not really possible to answer your question without more details on your program:

  • Do multiple threads modify the state of your sharedData variable?
  • If so, do you synchronize on all writes (and reads) of sharedData?

Using a ConcurrentHashMap only guarantees that the individual methods of the Map are thread-safe, it doesn't make an operation such as this thread-safe:

if (!map.containsKey("foo")) {
    map.put("foo", bar);
}
Share:
33,488
pschang
Author by

pschang

Updated on November 14, 2020

Comments

  • pschang
    pschang over 3 years

    I've read around quite a bit but haven't found a definitive answer.

    I have a class that looks like this:

        public class Foo() {
    
            private static final HashMap<String, HashMap> sharedData;
    
            private final HashMap myRefOfInnerHashMap;
    
            static {
               // time-consuming initialization of sharedData
               final HashMap<String, String> innerMap = new HashMap<String, String>;
               innerMap.put...
               innerMap.put...
               ...a
    
               sharedData.put(someKey, java.util.Collections.unmodifiableMap(innerMap));
            }
    
            public Foo(String key) {
                this.myRefOfInnerHashMap = sharedData.get(key);
            }
    
            public void doSomethingUseful() {
                // iterate over copy
                for (Map.Entry<String, String> entry : this.myRefOfInnerHashMap.entrySet()) {
                    ...
                }
            }
         }
    

    And I'm wondering if it is thread safe to access sharedData from instances of Foo (as is shown in the constructor and in doSomethingUseful()). Many instances of Foo will be created in a multi-threaded environment.

    My intention is that sharedData is initialized in the static initializer and not modified thereafter (read-only).

    What I've read is that immutable objects are inherently thread safe. But I've only seen this in what seems to be the context of instance variables. Are immutable static variables thread safe?

    The other construct I found was a ConcurrentHashMap. I could make sharedData of type ConcurrentHashMap but do the HashMaps it contains also have to be of type ConcurrentHashMap? Basically..

    private static final ConcurrentHashMap<String, HashMap> sharedData;
    

    or

    private static final ConcurrentHashMap<String, ConcurrentHashMap> sharedData;
    

    Or would it be safer (yet more costly to simply clone())?

    this.myCopyOfData = sharedData.get(key).clone();
    

    TIA.

    (Static initializer has been edited to give more context.)

  • Michael Borgwardt
    Michael Borgwardt about 14 years
    There is something inherently thread safe about a final static variable, since final means it can be assigned only once, and the assignment has to happen during class initialization, which is implicitly synchronized. Of course, that says nothing about objects referred to by such a variable, but e.g. a final static long or double is safe whereas an instance or non-final long or double is not.
  • Admin
    Admin about 14 years
    making anything final is inherently making that variable a thread safe reference
  • Xairoo
    Xairoo about 14 years
    It is not immutable. This seems to be a common misconception with final, it only prevents the reassignment of the sharedData.
  • matt b
    matt b about 14 years
    A final Map is not safe to be shared among many threads without synchronization. This is what I mean by "nothing inherently thread safe about a final static variable".
  • matt b
    matt b about 14 years
    @fuzzy lollipop, the reference might be thread safe, but what about the state inside the Map? That is what is key.
  • brady
    brady about 14 years
    As long as no subsequent changes are made, that state will be synchronized: java.sun.com/docs/books/jls/third_edition/html/…
  • matt b
    matt b about 14 years
    @erickson, I'm not referring to the static initializer, I'm referring to what is done with the Map(s) after object construction.
  • brady
    brady about 14 years
    I infer from the OP that no modifications are made to the map state outside of the static initializer. You'd only have a visibility problem if that's not true.
  • brady
    brady about 14 years
    @Robin - he points that out with, "any data inside it can be changed ... at any time."
  • Xairoo
    Xairoo about 14 years
    You are right, it is just poor wording. Apparently I cannot undo my downvote though...sorry!
  • pschang
    pschang about 14 years
    Yes, my apologizes on not giving complete context to my goals. I edited the question. My intention is that sharedData is not modified after the static initializer. I want to make the Maps that are contained in it thread-safe as well. I modified the code above to indicate that.
  • pschang
    pschang about 14 years
    No. I've seen plenty of answers to that. My question is when multiple threads access sharedData's contents, whether there will be any contention.
  • Igor Artamonov
    Igor Artamonov about 14 years
    sorry, english isn't my native language :( but i'm trying to fix it :)
  • Kevin Brock
    Kevin Brock about 14 years
    For increased clarity, I would recommend changing "access" to "update". If you can guarantee that all subsequent accesses are read-only through other means, then any class is thread safe (thus, some accesses are thread safe even when there is no locking or immutability). Designing for immutability makes it easier to prove the thread safety as it enforces this guarantee.
  • Mike Daniels
    Mike Daniels about 14 years
    @freshfunk, it is safe to iterate over a container whose contents never change. It is not safe if the contents can change.
  • pschang
    pschang about 14 years
    Yeah, that's what my intuition was telling me. Mostly because the documented pitfalls around thread-safe objects were around the modification of contents during usage by other threads. But I wasn't sure if there were internal constructs (such as a current index pointer during iteration) that would not be thread-safe. For example, reading a map to index of 5 and then another thread continuing its iteration and being in the wrong spot.
  • Mike Daniels
    Mike Daniels about 14 years
    @freshfunk, That's not actually the problem. Iterators themselves track their position within a collection. The issue is that if the contents of the collection changes during iteration, the position of an iterator is no longer meaningful. The item that it was pointing at may have shifted forward or backward, may be deleted, etc. This is why most standard Java iterators are "fail-fast". They can detect if the collection has changed since iteration was started and throw a ConcurrentModificationException instead of producing unexpected results.
  • extraneon
    extraneon about 14 years
    @Michael : Formally you are correct. But practically speaking the contents of a Map is not constant be default.