从高速数据流更新地图

时间:2011-01-23 00:17:29

标签: java multithreading concurrenthashmap

我有一个多线程Java应用程序,其中方法[update(key, value)]更新ConcurrentHashMap。对于每个键,接收的值将多于可以放入映射中的值,因此一旦更新了键,则只应使用等待的线程的最新值,然后再次更新映射。或者也许有一种锁可以在只有1个线程等待的地方使用 - 最后一个到达锁的那个(有效地处理已经等待的线程)?重要的是整个映射没有被锁定,这就是为什么我没有在正常HashMap周围使用同步块,因为即使有线程等待密钥A,仍应允许密钥B更新为只要没有线程已经更新为B存储的值

更简洁地说,如何使用上次接收的值作为下次更新来更新接收键值对的地图,而不是更新的地图?因此,在时间A更新为1时,接收值为5,3,6,8,这意味着A的下一次更新将为8.

2 个答案:

答案 0 :(得分:0)

这是一个难题,困难的根源在于捕获更新到达的顺序。

如果更新已经具有关联的(细粒度)时间戳,那么解决方案非常简单:

  1. 定义一个包含实际值和时间戳的Value类。它需要一个同步的setIfNewer(ActualValue v, Timestamp t),如果提供的时间戳更新,它将更新实际值。
  2. 将地图定义为ConcurrentHashMap<Key, Value>
  3. 使用putIfAbsent将值放入地图中。如果putIfAbsent()返回非空值,请使用setIfNewer(...)进行更新。
  4. 请注意,这仅适用于地图更新可以长期保持 ;即平均数据速率不是太高而无法应付。

    如果更新没有有关联的时间戳,那么您就遇到了问题。如果您无法跟上更新,那么您将难以在更新中添加时间戳,以准确反映到达时间。这意味着存在更新(实际上)重新排序的风险。 (如果是这种情况,那么我不认为问题是可以解决的......不改变问题;见下文。)

    可能有用的一些事情:

    • 进行一些性能分析/性能分析,找出瓶颈的确切位置。它可能根本不在进行地图更新。 (毕竟ConcurrentHashMap的设计具有高度可扩展性。)

    • 如果线程和键值之间存在强烈的亲和关系,那么您可以尝试1)使用每线程LRU映射对每个线程中的更新进行重复数据删除,或者2)使用每线程计数器而不是时间戳。

    • 您可以尝试根据键空间对地图进行分区。

    • 您可以尝试添加更多处理器和/或更多内存......具体取决于您的分析和监控报告的内容。

    • 您可以尝试根据键空间对整个应用程序进行分区。如果真正的问题是应用程序无法跟上,这可能是唯一可行的方法。

答案 1 :(得分:0)

怎么做?

有一个相当简单的解决方案来实现一个音序器,你添加的每个对象都需要一个在构造时分配的长字段,如AtomicLong.getAndIncrement()。

更新看起来像并且不需要同步

Class Value{
private static final AtomicLong sequencer = new AtomicLong()
final long seq = sequencer.getAndIncrement():
public boolean equals(Object o){
  //include seq as well :)
}
....
}
ConcurrentMap map;
for (Value cur;;){
    cur = map.get(key);
    if (cur==null){
        if (null==(cur=map.putIfAbsent(key, value))){
            break;
        }
    }           
    if (cur.seq>=value.seq){
        break;
    }
    if (map.replace(key, cur, value))
        break;
}