代码之家  ›  专栏  ›  技术社区  ›  CPerkins

ConcurrentHashMap条件替换

  •  4
  • CPerkins  · 技术社区  · 15 年前

    public class PriceTick {
       final String instrumentId;
       ...
       final long timestamp;
       ...
    

    还有一个类(我们称之为TickHolder),它拥有一个ConcurrentHashMap(我们称之为 地图 ).

    我希望能够实现一个条件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源代码的初始读取使我认为外部同步及其内部锁不会很好地工作,因此至少,执行结构更改和包含类执行的每个映射方法都必须由包含类同步。。。即使那样,我也会相当不安。

    我曾想过将ConcurrentHashMap子类化,但这似乎是不可能的。它使用具有默认访问权限的内部final类HashEntry,因此尽管ConcurrentHashMap不是final,但它不可扩展。

    这似乎意味着,为了编写条件替换方法,我必须返回到将TickHolder实现为包含一个老式HashMap。

    那么,问题是:我的上述观点正确吗?我是否(希望)错过了一些可以导致不同结论的东西,无论是明显的还是微妙的?我真的很想能够利用这个可爱的条纹锁定机制在这里。

    3 回复  |  直到 15 年前
        1
  •  4
  •   AmirAShabani David R. Longnecker    6 年前

    非确定性的解决方案是循环 replace() :

    do {
      PriceTick oldTick = map.get(newTick.getInstrumentId());
    } while ((oldTick == null || oldTick.before(newTick)) && !map.replace(newTick.getInstrumentId(), oldTick, newTick);
    

    虽然这看起来很奇怪,但这是一种常见的建议模式。

        2
  •  2
  •   robSE13    11 年前

    @克莱特斯解决方案为我解决一个几乎相同的问题奠定了基础。我认为需要进行一些更改,尽管好像oldTick为null,那么replace抛出了一个null点异常,如@hotzen所述

    PriceTick oldTick;
    do {
      oldTick = map.putIfAbsent(newTick.getInstrumentId());
    } while (oldTick != null && oldTick.before(newTick) && !map.replace(newTick.getInstrumentId(), oldTick, newTick);
    
        3
  •  2
  •   Drpmma    5 年前

    正确答案应该是

    PriceTick oldTick;
    do {
      oldTick = map.putIfAbsent(newTick.getInstrumentId(), newTick);
      if (oldTick == null) {
        break;
      }
    } while (oldTick.before(newTick) && !map.replace(newTick.getInstrumentId(), oldTick, newTick);
    
        4
  •  1
  •   Sbodd    15 年前

    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);
    }