具有多个读取和定期写入的HashMap

时间:2017-02-01 16:50:03

标签: java multithreading

有一个读密集用例,其中多个线程正在从HashMap读取,并且每30分钟让它说它过期并更新整个地图。 地图的最大尺寸范围为2MB到200MB。

所以,目前我正在考虑的解决方案是让HashMap拥有多个读者,一旦它到期,守护程序线程将从数据源获取数据并创建一个新的地图,一旦完成它将锁定旧的HashMap然后将新创建的地图复制到旧地图。 这是正确的方法,如果是的话,有更好的方法,如果没有,什么是正确的方法。将数据复制到新地图会花费更多时间吗?

目的是提供最大的读取请求。

2 个答案:

答案 0 :(得分:0)

首先,要注意链接的问题(来自上面的评论)及其答案。在此重复:Is it safe to get values from a java.util.HashMap from multiple threads (no modification)?

由于您已经构建了一个新的(显然是完整的)映射来替换旧映射,因此请不要更新现有的散列映射。这会更慢,并且在您更新时会阻止对地图的访问。

只需用新的地图替换旧地图:

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()返回的任何地图中读取,并且永远不要尝试更新它。再次,请参阅关联的问题:Is it safe to get values from a java.util.HashMap from multiple threads (no modification)?

唯一的缺点是线程可以获取旧映射,并在该线程仍在使用旧映射时将其替换为访问控制器对象。如果您必须要求在生成新地图后对哈希地图数据进行所有访问必须仅使用新地图中的数据,则此方法将无效。然后,您必须锁定整个地图并将其更新到位。这将是很多更慢。

更新哈希映射非常繁琐。首先,如何确保删除旧地图中不在新地图中的条目?

答案 1 :(得分:0)

  

所以,目前我正在考虑的解决方案是拥有一个包含多个读取器的hashmap,一旦它到期,守护程序线程将从数据源获取数据并创建一个新地图,一旦完成它将锁定旧的HashMap,然后将新创建的地图复制到旧地图。

听起来不错。如果您在消息中暗示没有人在创建HashMap后修改,只要您正确共享它,就可以安全地将它用于多个线程。为确保不变性,您应使用Collection.unmodifiableMap(map)包装地图。

要与线程共享地图,您需要使其成为所有线程都可以访问的易失性字段。类似的东西:

protected volatile HashMap map = null;

如果它是volatile,那么您不需要进行任何锁定。您更新方法然后看起来像:

// no need to have synchronized here
HashMap updateMap( HashMap newMap ) {
   HashMap oldMap = this.map;
   this.map = Collection.unmodifiableMap(newMap);
   return oldMap;
}

您的其他方法也不一定都是synchronizedvolatile字段将执行得更好,因为线程只有在访问共享映射时才会跨越读取内存屏障,而在更新时只会跨越写入内存屏障。使用synchronized关键字,线程每次都会跨越读写屏障。更糟糕的是,synchronized关键字具有锁定开销,可以保证您不需要的互斥锁。

  

将数据复制到新地图会花费更多时间吗?

将数据复制到新地图需要花费时间,但不超过HashMaps之间典型的数据复制。 的不同之处在于volatile字段访问可能比直接字段访问慢得多,因为存在跨越的内存障碍。