Java中final静态变量是线程安全的吗?
我已经阅读了很多,但还没有找到明确的答案。
我有一个如下所示的类:
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()) {
...
}
}
}
我想知道从 Foo 实例访问共享数据是否是线程安全的(如构造函数和 doSomethingUseful() 中所示)。 Foo 的许多实例将在多线程环境中创建。
我的意图是,sharedData 在静态初始化程序中初始化,此后不进行修改(只读)。
我读到的是,不可变对象本质上是线程安全的。但我只在实例变量的上下文中看到了这一点。不可变静态变量线程安全吗?
我发现的另一个构造是 ConcurrentHashMap。我可以将共享数据设为 ConcurrentHashMap 类型,但是它包含的 HashMap 也必须是 ConcurrentHashMap 类型吗?基本上..
private static final ConcurrentHashMap<String, HashMap> sharedData;
或者
private static final ConcurrentHashMap<String, ConcurrentHashMap> sharedData;
或者它会更安全(但简单地克隆()成本更高)?
this.myCopyOfData = sharedData.get(key).clone();
TIA。
(静态初始值设定项已被编辑以提供更多上下文。)
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.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
最终的对
sharedData
的引用是线程安全的,因为它永远不能更改。 Map 的内容不是线程安全,因为它需要最好用 GuavaImmutableMap
实现或java.util.Collections.unmodifyingMap()< /code> 或使用
java.util.concurrent
包中的 Map 实现之一。只有两者都做到了,您才能在地图上拥有全面的线程安全性。任何包含的映射都需要是不可变的或者是并发实现之一。
.clone() 从根本上被破坏了,默认情况下远离
克隆是浅克隆,它只会返回对容器对象的引用,而不是完整的副本。关于原因的普遍可用信息中有详细记录。
the reference to
sharedData
which is final is thread safe since it can never be changed. The contents of the Map is NOT thread safe because it needs to be either wrapped with preferably a GuavaImmutableMap
implementation orjava.util.Collections.unmodifiableMap()
or use one of the Map implementations in thejava.util.concurrent
package.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.
.clone() is fundamentally broken, stay away
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.
静态初始化块中静态最终字段的初始化是线程安全的。但是,请记住,静态最终引用指向的对象可能不是是线程安全的。如果您引用的对象是线程安全的(例如,它是不可变的),那么您就没有问题。
外部 HashMap 中包含的每个单独的 HashMap 不能保证线程安全,除非您按照问题中的建议使用 ConcurrentHashMap。如果不使用线程安全的内部 HashMap 实现,则当两个线程访问同一个内部 HashMap 时,可能会得到意想不到的结果。请记住,只有 ConcurrentHashMap 上的某些操作是同步的。例如,迭代不是线程安全的。
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.
什么是线程安全?当然,HashMap 的初始化是线程安全的,因为所有 Foo 共享同一个 Map 实例,并且保证 Map 存在,除非静态初始化中发生异常。
但修改 Map 的内容肯定不是线程安全的。 staticfinal意味着Map的sharedData不能切换为另一个Map。但地图的内容是另一个问题。如果给定的密钥同时使用多次,您可能会遇到并发问题。
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.
是的,这也是线程安全的。静态类的所有最终成员都将在任何线程被允许访问它们之前被初始化。
如果
static
块在初始化期间失败,则在首次尝试初始化的线程中将引发ExceptionInInitializerError
。随后尝试引用该类将引发NoClassDefFoundError
。一般来说,
HashMap
的内容不能保证跨线程的可见性。但是,类初始化代码使用synchronized
块来防止多个线程初始化该类。此同步将刷新映射的状态(及其包含的HashMap
实例),以便它们对所有线程都正确可见 - 假设没有对映射或它所包含的映射进行任何更改包含在类初始值设定项之外。请参阅 Java 语言规范,第 12.4.2 节< /a> 有关类初始化和同步要求的信息。
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, anExceptionInInitializerError
will be raised in the thread that first attempts initialization. Subsequent attempt to reference the class will raise aNoClassDefFoundError
.In general, the contents of a
HashMap
have no guarantee of visibility across threads. However, the class initialization code uses asynchronized
block to prevent multiple threads from initializing the class. This synchronization will flush the state of the map (and theHashMap
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.
不,除非它们是不可变的。
他们唯一做的就是
不过,如果您的属性是可变的,那么它就不是线程安全的。
另请参阅:我们是否同步最终的实例变量?
除了它们是类级别之外,它完全相同。
No. Except if they are immutable.
The only thing they do is
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.
final static
变量本身不具有线程安全性。声明成员变量final static
仅确保该变量仅被赋值一次。线程安全问题与如何声明变量关系不大,而是取决于如何与变量交互。因此,如果没有有关程序的更多详细信息,实际上不可能回答您的问题:
sharedData
变量的状态?sharedData
的所有写入(和读取)?使用 ConcurrentHashMap 只能保证 Map 的各个方法是线程安全的,它不会使像这样的操作成为线程安全的:
There is nothing inherently thread safe about a
final static
variable. Declaring a member variablefinal 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:
sharedData
variable?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:您实际上不是在问
sharedData
的静态初始化是否是线程安全的并且仅执行一次吗?是的,情况就是如此。
当然这里很多人都正确地指出了
sharedData
的内容仍然可以修改。Aren't you are actually asking if the static initialization of
sharedData
is thread safe and only executed once?And yes, that is the case.
Of course many people here have correctly pointed out that the contents of
sharedData
can still be modified.在这种情况下,只有共享数据对象是不可变的,这意味着您将始终使用同一个对象。但其中的任何数据都可以随时从任何线程更改(删除、添加等)。
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.