【发布时间】:2020-12-04 09:09:51
【问题描述】:
我们有这样的案例。
class A{
class foo{
//Map with a lot of entries
private HashMap<String,String> dataMap;
public updater(){
// updates dataMap
// takes several milliseconds
}
public someAction(){
// needs to perform read on dataMap
// several times, in a long process
// which takes several milliseconds
}
}
问题是,someAction 和 updater 都可以同时调用,someAction 是一个更频繁的方法。如果调用 updater,它可以从 dataMap 中替换很多值。我们需要 readAction 的一致性。如果方法以旧 dataMap 开始,则所有读取都应使用旧 dataMap。
class foo{
//Map with a lot of entries
private HashMap<String,String> dataMap;
public updater(){
var updateDataMap = clone(dataMap); // some way to clone data from map
// updates updateDataMap instead of dataMap
// takes several milliseconds
this.dataMap = updateDataMap;
}
public someAction(){
var readDataMap = dataMap;
// reads from readDataMap instead of dataMap
// several times, in a long process
// which takes several milliseconds
}
}
这能确保一致性吗?我相信克隆方法会在内存中分配不同的区域,并且会从那里发生新的引用。是否会对性能产生任何影响?而oldDataMap的内存用完后会释放吗?
如果这是正确的方法,有没有其他有效的方法可以达到同样的效果?
【问题讨论】:
-
您需要将
dataMap声明为volatile。在每次更新时克隆整个地图是否比使用并发地图更好或更差,取决于实际用例。 -
将(最终)释放不再可访问的内存。
-
性能取决于几个因素,包括地图深层副本的大小、可用内存量以及
update()和readAction()地图所需的时间。您应该指定应用程序中的映射一致性是否意味着updater()已从头到尾运行 或 查看部分updater()结果是可以的,只要每个set(key, value)操作是原子的。在第二种情况下,使用synchronizedMap(dataMap)包装地图避免了复制的开销,并使volatile不再需要。 -
此代码不安全。即使映射内部没有可变状态,您也有多个线程访问
dataMap变量而没有协调。这里可能出错的地方比人们最初想象的要多得多。 (即使有使dataMap易变的“修复”,并且地图一旦发布就永远不会修改,您仍然面临丢失更新的风险。) -
@BrianGoetz 你说的是多线程调用
updater()还是别的什么的场景?
标签: java parallel-processing java-11 openjdk-11