0

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

更简洁地说,如何使用最后接收到的值作为下一次更新来更新接收键值对的速度比更新速度更快的映射?因此,在 A 更新为 1 时,将收到 5、3、6、8 的值,这意味着 A 的下一次更新将是 8。

4

2 回答 2

0

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

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

  1. 定义一个Value保存实际值和时间戳的类。如果提供的时间戳更新,它需要一个同步setIfNewer(ActualValue v, Timestamp t)更新实际值。
  2. 将地图定义为ConcurrentHashMap<Key, Value>
  3. 用于putIfAbsent将值放入地图。如果putIfAbsent()返回一个非空值,使用setIfNewer(...)更新它。

请注意,这仅在地图更新能够长期保持同步时才有效;即平均数据速率不是太高而无法应付。

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

一些可能有效的事情:

  • 做一些分析/性能分析来找出瓶颈到底在哪里。它可能根本不在进行地图更新。(毕竟ConcurrentHashMap设计为高度可扩展的。)

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

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

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

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

于 2011-01-23T02:08:16.837 回答
0

怎么做?

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

update 看起来像这样,不需要 sync

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;
}
于 2011-01-24T11:37:26.103 回答