Java中的最终静态变量线程安全吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2412124/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 07:16:25  来源:igfitidea点击:

Are final static variables thread safe in Java?

javamultithreadingstatichashmapfinal

提问by pschang

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.

我想知道从 Foo 实例访问 sharedData 是否是线程安全的(如构造函数和 doSomethingUseful() 中所示)。Foo 的许多实例将在多线程环境中创建。

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

我的意图是 sharedData 在静态初始值设定项中初始化并且此后不会修改(只读)。

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..

我发现的另一个构造是 ConcurrentHashMap。我可以创建 ConcurrentHashMap 类型的 sharedData 但它包含的 HashMaps 也必须是 ConcurrentHashMap 类型吗?基本上..

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())?

或者它会更安全(但简单地 clone() 成本更高)?

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

TIA.

TIA。

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

(静态初始值设定项已被编辑以提供更多上下文。)

采纳答案by Mike Daniels

the referenceto sharedDatawhich is final is thread safe since it can never be changed. The contents of the Map is NOTthread safe because it needs to be either wrapped with preferably a Guava ImmutableMapimplementation or java.util.Collections.unmodifiableMap()or use one of the Map implementations in the java.util.concurrentpackage.

参考sharedData这是最后的是线程安全的,因为它永远不会改变。Map 的内容不是线程安全的,因为它需要用最好的 GuavaImmutableMap实现包装java.util.Collections.unmodifiableMap()或使用java.util.concurrent包中的 Map 实现之一。

Only if you do BOTHwill you have comprehensive thread safety on the Map. Any contained Maps need to be immutable or one of the concurrent implementations as well.

只有当你做BOTH将你对地图综合线程安全。任何包含的 Map 都需要是不可变的,或者也是并发实现之一。

.clone() is fundamentally broken, stay away

.clone() 从根本上被破坏了,远离

cloning by default is a shallow clone, it will just return references to container objects not complete copies. It is well documented in generally available information on why.

默认情况下克隆是浅克隆,它只会返回对容器对象的引用,而不是完整的副本。在一般可用的信息中详细记录了原因。

回答by erickson

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 staticblock fails during initialization, an ExceptionInInitializerErrorwill be raised in the thread that first attempts initialization. Subsequent attempt to reference the class will raise a NoClassDefFoundError.

如果static块在初始化期间失败,ExceptionInInitializerError将在第一次尝试初始化的线程中引发。后续尝试引用该类将引发NoClassDefFoundError.

In general, the contents of a HashMaphave no guarantee of visibility across threads. However, the class initialization code uses a synchronizedblock to prevent multiple threads from initializing the class. This synchronization will flush the state of the map (and the HashMapinstances 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.

通常,a 的内容HashMap不能保证跨线程的可见性。但是,类初始化代码使用一个synchronized块来防止多个线程初始化类。此同步将刷新映射(及其HashMap包含的实例)的状态,以便它们对所有线程正确可见 - 假设在类初始值设定项之外未对映射或其包含的映射进行任何更改。

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

有关类初始化和同步要求的信息请参阅Java 语言规范第 12.4.2 节

回答by matt b

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

final static变量本身并没有线程安全的东西。声明一个成员变量final static只能确保这个变量只被分配一次。

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 sharedDatavariable?
  • If so, do you synchronize on all writes (andreads) of sharedData?
  • 多个线程是否会修改sharedData变量的状态?
  • 如果是这样,您是否同步所有的写入(读取)sharedData

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

使用 ConcurrentHashMap 仅保证 的各个方法Map是线程安全的,它不会使诸如此线程安全的操作:

if (!map.containsKey("foo")) {
    map.put("foo", bar);
}

回答by Igor Artamonov

At this case only sharedData object is immmutable, that means only that all the time you will work with same object. But any data inside it can be changed (removed, added, etc) at any time, from any thread.

在这种情况下,只有 sharedData 对象是不可变的,这意味着您将始终使用同一个对象。但是其中的任何数据都可以随时从任何线程更改(删除、添加等)。

回答by Mike Daniels

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 notbe thread safe. If the object to which you refer is thread safe (e.g., it's immutable), you're in the clear.

静态初始化块中静态 final 字段的初始化是线程安全的。但是,请记住静态最终引用指向的对象可能不是线程安全的。如果您引用的对象是线程安全的(例如,它是不可变的),那么您就清楚了。

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.

除非您按照问题中的建议使用 ConcurrentHashMap,否则不能保证外部 HashMap 中包含的每个单独的 HashMap 都是线程安全的。如果您不使用线程安全的内部 HashMap 实现,则当两个线程访问同一个内部 HashMap 时,您可能会得到意想不到的结果。请记住,只有 ConcurrentHashMap 上的一些操作是同步的。例如,迭代不是线程安全的。

回答by extraneon

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.

什么是线程安全?当然,HashMap 的初始化是线程安全的,因为所有 Foo 共享相同的 Map 实例,并且保证 Map 存在,除非静态初始化中发生异常。

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.

但是修改 Map 的内容肯定不是线程安全的。static final 意味着 Map sharedData 不能切换到另一个 Map。但是地图的内容是一个不同的问题。如果给定的键同时使用多次,您可能会遇到并发问题。

回答by OscarRyz

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.

除了它们是类级别之外,它完全相同。

回答by Thirler

Aren't you are actually asking if the static initialization of sharedDatais thread safe and only executed once?

您实际上不是在问 的静态初始化是否sharedData是线程安全的并且只执行一次?

And yes, that is the case.

是的,就是这样。

Of course many people here have correctly pointed out that the contents of sharedDatacan still be modified.

当然这里很多人已经正确指出了,里面的内容sharedData还是可以修改的。