我希望能够有条件地替换ConcurrentHashMap中的值。那就是:
public class PriceTick { final String instrumentId; ... final long timestamp; ...
一个拥有ConcurrentHashMap的类(我们称之为TickHolder)(我们只需将其命名为 map )。
我希望能够实现条件put方法,这样如果没有键的条目,则插入新的条目,但是如果存在现有条目,则只有在插入时才会插入新条目新PriceTick中的时间戳值大于现有值。
对于老式的HashMap解决方案,TickHolder会有一个put方法:
public void add(PriceTick tick) { synchronized(map) { if ((map.get(tick.instrumentId) == null) || (tick.getTimestamp() > map.get(tick.instrumentId).getTimestamp()) ) map.put(tick.instrumentId, tick); } }
使用ConcurrentHashMap,人们可能希望删除同步并使用一些原子方法,如replace,但这是无条件的。很明显,必须编写“条件替换”方法。
但是,由于测试和替换操作是非原子的,为了保证线程安全,它必须同步 - 但我最初读取的ConcurrentHashMap源使我认为外部同步及其内部锁不会很好地工作,所以至少每个执行结构更改的Map方法和包含类的执行必须由包含类同步......即使这样,我也会感到非常不安。
我想过继承ConcurrentHashMap,但这似乎是不可能的。它使用具有默认访问权限的内部最终类HashEntry,因此尽管ConcurrentHashMap不是final,但它不可扩展。
这似乎意味着我必须回到将TickHolder实现为包含一个老派的HashMap才能编写我的条件替换方法。
所以,问题是:我对上述内容是否正确?我(希望)是否遗漏了一些明显或微妙的东西,这会产生不同的结论?我真的希望能够在这里使用那种可爱的条纹锁定机制。
答案 0 :(得分:4)
非确定性解决方案是循环replace()
:
do {
PriceTick oldTick = map.get(newTick.getInstrumentId());
} while ((oldTick == null || oldTick.before(newTick)) && !map.replace(newTick.getInstrumentId(), oldTick, newTick);
虽然看起来很奇怪,但这是一种常见的建议模式。
答案 1 :(得分:2)
@cletus解决方案为我解决几乎完全相同的问题奠定了基础。我认为需要进行一些更改,但是如果oldTick为null则替换会抛出NullPointerException,如@hotzen所述
PriceTick oldTick;
do {
oldTick = map.putIfAbsent(newTick.getInstrumentId());
} while (oldTick != null && oldTick.before(newTick) && !map.replace(newTick.getInstrumentId(), oldTick, newTick);
答案 2 :(得分:2)
正确答案应该是
PriceTick oldTick;
do {
oldTick = map.putIfAbsent(newTick.getInstrumentId(), newTick);
if (oldTick == null) {
break;
}
} while (oldTick.before(newTick) && !map.replace(newTick.getInstrumentId(), oldTick, newTick);
答案 3 :(得分:1)
作为替代方案,您是否可以创建TickHolder类,并将其用作地图中的值?它使地图使用起来稍微麻烦(现在获取一个值是map.getValue(key).getTick()),但它可以让你保持ConcurrentHashMap的行为。
public class TickHolder {
public PriceTick getTick() { /* returns current value */
public synchronized PriceTick replaceIfNewer (PriceTick pCandidate) { /* does your check */ }
}
你的put方法就像:
public void updateTick (PriceTick pTick) {
TickHolder value = map.getValue(pTick.getInstrumentId());
if (value != null) {
TickHolder newTick = new TickHolder(pTick);
value = map.putIfAbsent(pTick.getInstrumentId(), newTick);
if (value == null) {
value = newTick;
}
}
value.replaceIfNewer(pTick);
}