我有一个多线程Java应用程序,其中方法[update(key, value)]
更新ConcurrentHashMap
。对于每个键,接收的值将多于可以放入映射中的值,因此一旦更新了键,则只应使用等待的线程的最新值,然后再次更新映射。或者也许有一种锁可以在只有1个线程等待的地方使用 - 最后一个到达锁的那个(有效地处理已经等待的线程)?重要的是整个映射没有被锁定,这就是为什么我没有在正常HashMap
周围使用同步块,因为即使有线程等待密钥A,仍应允许密钥B更新为只要没有线程已经更新为B存储的值
更简洁地说,如何使用上次接收的值作为下次更新来更新接收键值对的地图,而不是更新的地图?因此,在时间A更新为1时,接收值为5,3,6,8,这意味着A的下一次更新将为8.
答案 0 :(得分:0)
这是一个难题,困难的根源在于捕获更新到达的顺序。
如果更新已经具有关联的(细粒度)时间戳,那么解决方案非常简单:
Value
类。它需要一个同步的setIfNewer(ActualValue v, Timestamp t)
,如果提供的时间戳更新,它将更新实际值。ConcurrentHashMap<Key, Value>
。putIfAbsent
将值放入地图中。如果putIfAbsent()
返回非空值,请使用setIfNewer(...)
进行更新。请注意,这仅适用于地图更新可以长期保持 ;即平均数据速率不是太高而无法应付。
如果更新没有有关联的时间戳,那么您就遇到了问题。如果您无法跟上更新,那么您将难以在更新中添加时间戳,以准确反映到达时间。这意味着存在更新(实际上)重新排序的风险。 (如果是这种情况,那么我不认为问题是可以解决的......不改变问题;见下文。)
可能有用的一些事情:
进行一些性能分析/性能分析,找出瓶颈的确切位置。它可能根本不在进行地图更新。 (毕竟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;
}