简体   繁体   English

Java中的最终静态变量是否安全?

[英]Are final static variables thread safe in Java?

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()). 我想知道从Foo实例访问sharedData是否是线程安全的(如构造函数和doSomethingUseful()中所示)。 Many instances of Foo will be created in a multi-threaded environment. 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. 我找到的另一个构造是ConcurrentHashMap。 I could make sharedData of type ConcurrentHashMap but do the HashMaps it contains also have to be of type ConcurrentHashMap? 我可以创建ConcurrentHashMap类型的sharedData但是它包含的HashMaps也必须是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. TIA。

(Static initializer has been edited to give more context.) (已编辑静态初始化程序以提供更多上下文。)

the reference to sharedData which is final is thread safe since it can never be changed. 最终的sharedData 引用是线程安全的,因为它永远不会被更改。 The contents of the Map is NOT thread safe because it needs to be either wrapped with preferably a Guava ImmutableMap implementation or java.util.Collections.unmodifiableMap() or use one of the Map implementations in the java.util.concurrent package. Map的内容不是线程安全的,因为它需要最好用Guava ImmutableMap实现或java.util.Collections.unmodifiableMap()包装,或者使用java.util.concurrent包中的Map实现之一。

Only if you do BOTH will you have comprehensive thread safety on the Map. 只有当你做双方将你对地图综合线程安全。 Any contained Maps need to be immutable or one of the concurrent implementations as well. 任何包含的Maps都需要是不可变的或者是并发实现之一。

.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. 有关原因的一般可用信息中有详细记录。

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 (eg, 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. 除非您按照问题中的建议使用ConcurrentHashMap,否则不能保证外部HashMap中包含的每个HashMap都是线程安全的。 If you do not use a thread-safe inner HashMap implementation, you may get unintended results when two threads access the same inner HashMap. 如果不使用线程安全的内部HashMap实现,当两个线程访问相同的内部HashMap时,可能会出现意外结果。 Keep in mind that only some operations on ConcurrentHashMap are synchronized. 请记住,只有ConcurrentHashMap上的某些操作是同步的。 For example, iteration is not thread-safe. 例如,迭代不是线程安全的。

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实例,并且除非静态init中发生异常,否则Map保证在那里。

But modifying the contents of the Map is most assuredly not thread safe. 但是,修改Map的内容绝对不是线程安全的。 Static final means that the Map sharedData can not be switched for another Map. 静态final意味着无法为另一个Map切换Map sharedData。 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. 如果给定的密钥同时使用多次,则可能会出现并发问题。

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. 如果static块在初始化期间失败,则会在首次尝试初始化的线程中引发ExceptionInInitializerError Subsequent attempt to reference the class will raise a NoClassDefFoundError . 随后尝试引用该类将引发NoClassDefFoundError

In general, the contents of a HashMap have no guarantee of visibility across threads. 通常, HashMap的内容无法保证跨线程的可见性。 However, the class initialization code uses a synchronized block to prevent multiple threads from initializing the class. 但是,类初始化代码使用synchronized块来防止多个线程初始化类。 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. 此同步将刷新映射的状态(以及它包含的HashMap实例),以便它们对所有线程都是正确可见的 - 假设在类初始值设定项之外没有对映射或它包含的映射进行任何更改。

See the Java Language Specification, §12.4.2 for information about class initialization and the requirement for synchronization. 有关类初始化和同步要求的信息请参阅Java语言规范,第12.4.2节

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. 除了属于班级之外,它完全相同。

There is nothing inherently thread safe about a final static variable. 关于final static变量,没有任何固有的线程安全性。 Declaring a member variable final static only ensures that this variable is assigned to just once. 声明成员变量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 sharedData variable? 多个线程是否会修改sharedData变量的状态?
  • If so, do you synchronize on all writes ( and reads) of sharedData ? 如果是这样,你是否同步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: 使用ConcurrentHashMap只能保证Map的各个方法是线程安全的,它不会进行诸如此线程安全的操作:

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

Aren't you are actually asking if the static initialization of sharedData is thread safe and only executed once? 您是否真的在询问sharedData的静态初始化sharedData是线程安全的并且只执行一次?

And yes, that is the case. 是的,就是这样。

Of course many people here have correctly pointed out that the contents of sharedData can still be modified. 当然,这里很多人都正确地指出,仍然可以修改sharedData的内容。

At this case only sharedData object is immmutable, that means only that all the time you will work with same object. 在这种情况下,只有sharedData对象是immutable,这意味着只有你将使用相同的对象。 But any data inside it can be changed (removed, added, etc) at any time, from any thread. 但是任何线程中的任何数据都可以随时更改(删除,添加等)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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