Java 具有多次读取和定期写入的HashMap
有一个读密集型用例,其中多个线程正在从HashMap读取数据,每30分钟让我们假设它过期并更新整个map。 地图的最大大小在2MB到200MB之间 因此,目前我正在考虑的解决方案是拥有一个具有多个读卡器的HashMap,一旦它过期,守护进程线程将从数据源获取数据并创建一个新的映射,一旦它完成,将锁定旧的HashMap,然后将新创建的映射复制到旧的HashMap。 它是正确的方法吗?如果是,有更好的方法,如果不是,什么是正确的方法。将数据复制到新地图会花费更多时间吗Java 具有多次读取和定期写入的HashMap,java,multithreading,Java,Multithreading,有一个读密集型用例,其中多个线程正在从HashMap读取数据,每30分钟让我们假设它过期并更新整个map。 地图的最大大小在2MB到200MB之间 因此,目前我正在考虑的解决方案是拥有一个具有多个读卡器的HashMap,一旦它过期,守护进程线程将从数据源获取数据并创建一个新的映射,一旦它完成,将锁定旧的HashMap,然后将新创建的映射复制到旧的HashMap。 它是正确的方法吗?如果是,有更好的方法,如果不是,什么是正确的方法。将数据复制到新地图会花费更多时间吗 其目的是最大限度地满足阅读要求
其目的是最大限度地满足阅读要求。首先,请注意链接问题(来自上面的评论)及其答案。这里重复: 因为您已经在构建一个新的(显然是完整的)映射来替换旧的映射,所以不要就地更新现有的hashmap。这样会比较慢,并且在更新地图时会阻止对地图的访问 只需将旧地图替换为新地图:
public class HashMapAccessController
{
protected HashMap map = null;
// version - increment this on each update
// (assuming generation of a new map version
// takes measurable time, rollover is a
// problem for the next civilization...)
protected long version = 0;
public HashMapAccess( HashMap newMap )
{
map = newMap;
}
public synchronized long getVersion()
{
return( version );
}
synchronized HashMap getMap()
{
return( map );
}
synchronized HashMap updateMap( HashMap newMap )
{
version++;
HashMap oldMap = map;
map = newMap;
return( oldMap );
}
}
只需确保您只读取从getMap()
返回的任何映射,并且永远不要尝试更新它。同样,请参见链接问题:
唯一的缺点是线程可以获取旧映射,并在访问控制器对象中替换它,而该线程仍在使用旧映射。如果必须要求生成新映射后对哈希映射数据的所有访问必须仅使用来自新映射的数据,那么这种方法将不起作用。然后,您必须锁定整个地图并将其更新到位。那会慢得多
在适当的位置更新散列映射是乏味的。首先,如何确保删除旧地图中不在新地图中的条目
因此,目前我正在考虑的解决方案是拥有一个具有多个读卡器的hashmap,一旦它过期,守护进程线程将从数据源获取数据并创建一个新的映射,一旦它完成,将锁定旧的hashmap,然后将新创建的映射复制到旧的hashmap
听起来不错。如果,正如您在消息中所暗示的,HashMap创建后没有人修改它,那么只要您正确地共享它,您就可以安全地将它与多个线程一起使用。为了确保不变性,应该使用Collection.unmodifiableMap(map)
包装映射
要与线程共享映射,需要将其设置为所有线程都可以访问的易失性字段。比如:
protected volatile HashMap map = null;
由于它是易变的,因此不需要进行任何锁定。然后,更新方法如下所示:
// no need to have synchronized here
HashMap updateMap( HashMap newMap ) {
HashMap oldMap = this.map;
this.map = Collection.unmodifiableMap(newMap);
return oldMap;
}
您的其他方法都不需要同步<代码>易失性字段将执行得更好,因为线程在访问共享映射时只会跨越读取内存屏障,而在更新共享映射时只会跨越写入内存屏障。使用synchronized
关键字,线程每次都会跨越读写障碍。更糟糕的是,synchronized
关键字有锁开销,可以确保不需要的互斥
将数据复制到新地图会花费更多时间吗
将数据复制到新映射需要时间,但只需要在HashMap之间进行典型的数据复制。不同的是,
易失性
字段访问可能比直接字段访问慢得多,这是因为跨越了内存障碍。请参阅。@JonathanRosene问题说明了哈希映射
,因此。。。访问控制对象应该只使用Map
。见鬼,它可以只使用对象
@AndrewHenle:谢谢,是的,这种缺点是可以理解和接受的。再次感谢。@JonathanRosene:我没有使用treemap进行POC,但我怀疑由于大小有限,碰撞的机会较少,而且我不需要任何订单,hashmap可能会提供更好的性能。我会试试树形图。@AndrewHenle:有没有更好的方法来解决这个问题?我将定期从数据源中获取更新(删除、新、现有)记录的数据,然后我需要使用尽可能少的陈旧数据来处理读取请求,而不影响读取性能。@user3331132应用程序将对HashMap的引用保留多长时间?一旦构建了新的HashMap,只需几纳秒就可以将旧引用替换为新引用。对于一个几百MB的哈希映射,更新它可能需要几十秒,并且读卡器将被锁定在整个时间之外。这样看:如果一个基于新数据的操作只在几毫秒前启动,它会使用旧数据,您会认为它非常好。您真的关心最后几个操作可能会使用以前版本的数据吗?