有一种情况会构建一个地图,一旦它被初始化,它将永远不会被再次修改.但是,它将从多个线程访问(仅通过get(key)).java.util.HashMap以这种方式使用是否安全?
(目前,我很高兴使用a java.util.concurrent.ConcurrentHashMap,并没有明确的需要提高性能,但我只是好奇,如果一个简单HashMap就足够了.因此,这个问题不是 "我应该使用哪一个?"也不是性能问题.相反,问题是"它会安全吗?")
在阅读" 实践中的Java并发 "和" 实践中的OSGI "后,我发现了一个非常有趣的特定主题; 安全出版.以下内容来自JCIP:
要安全地发布对象,必须同时使对象的引用和对象的状态对其他线程可见.正确构造的对象可以通过以下方式安全发布:
- 从静态初始化程序初始化对象引用.
- 将对它的引用存储到volatile字段中.
- 将对它的引用存储到最终字段中.
- 将对它的引用存储到由(同步)锁定正确保护的字段中.
我的第一个问题:有多少java开发人员知道这个(问题)?有多少真实世界的Java应用程序真正遵循这个,这真的是一个真正的问题吗?我有一种感觉,99%的已实现的JVM不是那种"邪恶",即一个线程不能保证(事实上它实际上(几乎)"不可能")看到陈旧数据只是因为引用不遵循上面的"安全出版成语".
HashMap我的程序中有一个由多个线程访问,偶尔由一个线程设置.
例如:
Map<String, String> myMap = new HashMap<String, String>();
Run Code Online (Sandbox Code Playgroud)
这可以通过多个线程访问.每小时一次,一个线程调用:
myMap = myRefreshedVersionOfTheMap;
Run Code Online (Sandbox Code Playgroud)
所以我的问题是这是否是线程安全的.如果两个映射始终都有密钥"importantKey",那么读取线程是否可以在"importantKey"不存在时访问映射?
编辑:
感谢答案,我意识到这个问题实际上是独立的HashMap.这是一个关于对象引用分配的问题.
java multithreading reference thread-safety java-memory-model
假设我有一个对象如下:
Map<String, String> m = new HashMap<>();
Run Code Online (Sandbox Code Playgroud)
然后我按如下方式同步该对象并更改其引用:
synchronize(m){
m = new HashMap<>();
}
Run Code Online (Sandbox Code Playgroud)
有了这段代码,m 上的锁会发生什么情况?更新 m 代表的新对象是否仍然安全?或者锁本质上是在旧对象上?
java concurrency multithreading synchronized synchronized-block