Ced*_*tin 5 java optimization synchronization locking trove4j
我有一个庞大的Trove地图和一个我需要经常从多个线程调用的方法.大多数情况下,此方法应返回true.线程正在进行大量的运算,我注意到由于以下方法存在一些争用(这只是一个例子,我的实际代码有点不同):
synchronized boolean containsSpecial() {
return troveMap.contains(key);
}
Run Code Online (Sandbox Code Playgroud)
请注意,这是一个"仅附加"地图:一旦添加了一个键,就会永远停留在那里(这对我接下来的事情很重要).
我注意到通过将以上内容更改为:
boolean containsSpecial() {
if ( troveMap.contains(key) ) {
// most of the time (>90%) we shall pass here, dodging lock-acquisition
return true;
}
synchronized (this) {
return troveMap.contains(key);
}
}
Run Code Online (Sandbox Code Playgroud)
我的数字运算得到了20%的加速(经过多次运行验证,长时间运行等).
这种优化看起来是否正确(知道一旦钥匙存在,它将永远留在那里)?
这种技术的名称是什么?
编辑
更新地图的代码比containsSpecial()方法更频繁地调用,看起来像这样(我已经同步了整个方法):
synchronized void addSpecialKeyValue( key, value ) {
....
}
Run Code Online (Sandbox Code Playgroud)
此代码不正确.
Trove不处理并发使用本身; 就像那样java.util.HashMap.因此,HashMap即使看似无辜的只读方法containsKey()也可能抛出运行时异常,或者更糟糕的是,如果另一个线程同时修改了映射,则进入无限循环.我不知道Trove的内部,但是HashMap,当超过加载因子时,重新删除,或删除条目可能导致其他只读取的线程失败.
如果与锁管理相比,操作需要大量时间,则使用读写锁来消除序列化瓶颈将大大提高性能.在类文档中ReentrantReadWriteLock,有"Sample usages"; 您可以使用第二个示例RWDictionary作为指南.
在这种情况下,映射操作可能如此之快以至于锁定开销占主导地位.如果是这种情况,您需要在目标系统上进行配置,以查看synchronized块或读写锁是否更快.
无论哪种方式,重要的一点是您无法安全地删除所有同步,或者您将具有一致性和可见性问题.
它被称为错误锁定;-)实际上,它是双重检查锁定方法的一些变体.而这种方法的原始版本在Java中是完全错误的.
允许Java线程在其本地内存中保留变量的私有副本(想想:多核机器的核心本地缓存).除非发生某些同步,否则允许任何Java实现永远不会将更改写回全局内存.
因此,很可能你的一个线程有一个局部存储器,其中的troveMap.contains(key)计算结果为true.因此,它永远不会同步,它永远不会获得更新的内存.
另外,当contains()看到troveMap数据结构的不一致内存时会发生什么?
查找Java内存模型以获取详细信息.或者看看这本书:Java Concurrency in Practice.