有一种情况会构建一个地图,一旦它被初始化,它将永远不会被再次修改.但是,它将从多个线程访问(仅通过get(key)).java.util.HashMap以这种方式使用是否安全?
(目前,我很高兴使用a java.util.concurrent.ConcurrentHashMap,并没有明确的需要提高性能,但我只是好奇,如果一个简单HashMap就足够了.因此,这个问题不是 "我应该使用哪一个?"也不是性能问题.相反,问题是"它会安全吗?")
如果我有两个多线程访问HashMap,但保证他们永远不会同时访问同一个密钥,那还能导致竞争条件吗?
在Java Concurrency In Practice一书中,我们被告知可以通过编译器,JVM在运行时甚至由处理器重新排序程序的指令.因此,我们应该假设执行的程序不会以与我们在源代码中指定的顺序完全相同的顺序执行其指令.
但是,讨论Java内存模型的最后一章提供了一个先前发生的规则列表,指出了JVM保留了哪些指令排序.这些规则中的第一条是:
我相信"程序顺序"是指源代码.
我的问题:假设这个规则,我想知道什么指令可能实际重新排序.
"行动"定义如下:
Java内存模型是根据操作指定的,包括对变量的读取和写入,监视器的锁定和解锁,以及启动和连接线程.JMM定义了在程序中的所有操作之前调用的部分排序.为了保证执行动作B的线程可以看到动作A的结果(A和B是否出现在不同的线程中),必须在A和B之间的关系之前发生.在没有发生之前在两个之间进行排序操作,JVM可以随意重新排序.
其他提到的订单规则是:
在阅读" 实践中的Java并发 "和" 实践中的OSGI "后,我发现了一个非常有趣的特定主题; 安全出版.以下内容来自JCIP:
要安全地发布对象,必须同时使对象的引用和对象的状态对其他线程可见.正确构造的对象可以通过以下方式安全发布:
- 从静态初始化程序初始化对象引用.
- 将对它的引用存储到volatile字段中.
- 将对它的引用存储到最终字段中.
- 将对它的引用存储到由(同步)锁定正确保护的字段中.
我的第一个问题:有多少java开发人员知道这个(问题)?有多少真实世界的Java应用程序真正遵循这个,这真的是一个真正的问题吗?我有一种感觉,99%的已实现的JVM不是那种"邪恶",即一个线程不能保证(事实上它实际上(几乎)"不可能")看到陈旧数据只是因为引用不遵循上面的"安全出版成语".
我希望这不是一个愚蠢的问题......
我的代码中的代码类似于以下代码:
public class ConfigStore {
public static class Config {
public final String setting1;
public final String setting2;
public final String setting3;
public Config(String setting1, String setting2, String setting3) {
this.setting1 = setting1;
this.setting2 = setting2;
this.setting3 = setting3;
}
}
private volatile HashMap<String, Config> store = new HashMap<String, Config>();
public void swapConfigs(HashMap<String, Config> newConfigs) {
this.store = newConfigs;
}
public Config getConfig(String name) {
return this.store.get(name);
}
}
Run Code Online (Sandbox Code Playgroud)
处理请求时,每个线程将使用getConfig()函数从存储中请求使用配置.但是,定期(最有可能每隔几天),使用swapConfigs()函数更新和交换配置.调用swapConfigs()的代码不会保留对它传入的Map的引用,因为它只是解析配置文件的结果.
volatile商店实例变量仍然需要关键字吗?volatile关键字带来任何潜在的性能瓶颈,我应该知道的或可避免因为读取的速度大大超过写入的速度?非常感谢,