Map键级别上的Java并发锁定

Wil*_*oat 12 java concurrency multithreading concurrenthashmap

有通过调用putPrice方法更新价格的作家.读者正在使用getPrice以获得最新价格.hasChangedMethod返回一个布尔值,标识自上次getPrice调用以来价格是否已更改.

我正在寻找最快的解决方案.我试图在关键级别实现线程安全的一致读/写地图.

我认为锁定整个地图可能会导致性能问题,这就是为什么我决定在关键级别上制作它.不幸的是,它没有按预期工作并阻止整个地图.为什么?你能帮我弄清楚我在做错了什么吗?

更新:

我想我们可以总结两个问题:1.如果在更新过程中,如何提供对其余密钥的免费访问.2.我如何保证我的方法的原子操作,因为它们需要多次操作读/写.例如getPrice()- 获得价格和更新hasChanged标志.

PriceHolder.java

public final class PriceHolder {

    private ConcurrentMap<String, Price> prices;

    public PriceHolder() {
        this.prices = new ConcurrentHashMap<>();

        //Receive starting prices..
        Price EUR = new Price();
        EUR.setHasChangedSinceLastRead(true);
        EUR.setPrice(new BigDecimal(0));

        Price USD = new Price();
        USD.setHasChangedSinceLastRead(true);
        USD.setPrice(new BigDecimal(0));
        this.prices.put("EUR", EUR);
        this.prices.put("USD", USD);

    }

    /** Called when a price ‘p’ is received for an entity ‘e’ */
    public void putPrice(
            String e,
            BigDecimal p) throws InterruptedException {

            synchronized (prices.get(e)) {
                Price currentPrice = prices.get(e);
                if (currentPrice != null && !currentPrice.getPrice().equals(p)) {
                    currentPrice.setHasChangedSinceLastRead(true);
                    currentPrice.setPrice(p);
                } else {
                    Price newPrice = new Price();
                    newPrice.setHasChangedSinceLastRead(true);
                    newPrice.setPrice(p);
                    prices.put(e, newPrice);
                }
            }
    }

    /** Called to get the latest price for entity ‘e’ */
    public BigDecimal getPrice(String e) {
        Price currentPrice = prices.get(e);
        if(currentPrice != null){
            synchronized (prices.get(e)){
                currentPrice.setHasChangedSinceLastRead(false);
                prices.put(e, currentPrice);
            }
            return currentPrice.getPrice();
        }
        return null;
    }

    /**
     * Called to determine if the price for entity ‘e’ has
     * changed since the last call to getPrice(e).
     */
    public boolean hasPriceChanged(String e) {
        synchronized (prices.get(e)){
            return prices.get(e) != null ? prices.get(e).isHasChangedSinceLastRead() : false;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Price.java

public class Price {

    private BigDecimal price;

    public boolean isHasChangedSinceLastRead() {
        return hasChangedSinceLastRead;
    }

    public void setHasChangedSinceLastRead(boolean hasChangedSinceLastRead) {
        this.hasChangedSinceLastRead = hasChangedSinceLastRead;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    private boolean hasChangedSinceLastRead = false;

}
Run Code Online (Sandbox Code Playgroud)

Jim*_*myB 1

怎么样

class AtomicPriceHolder {

  private volatile BigDecimal value;
  private volatile boolean dirtyFlag;

  public AtomicPriceHolder( BigDecimal initialValue) {
    this.value = initialValue;
    this.dirtyFlag = true;
  }

  public synchronized void updatePrice( BigDecimal newPrice ) {
    if ( this.value.equals( newPrice ) == false) {
      this.value = newPrice;
      this.dirtyFlag = true;
    }
  }

  public boolean isDirty() {
    return this.dirtyFlag;
  }

  public BigDecimal peek() {
    return this.value;
  }

  public synchronized BigDecimal read() {
    this.dirtyFlag = false;
    return this.value;
  }

}
Run Code Online (Sandbox Code Playgroud)

...

public void updatePrice( String id, BigDecimal value ) {

  AtomicPriceHolder holder;
  synchronized( someGlobalSyncObject ) {
    holder = prices.get(id);
    if ( holder == null ) {
      prices.put( id, new AtomicPriceHolder( value ) );
      return;
    }
  }

  holder.updatePrice( value );

}
Run Code Online (Sandbox Code Playgroud)

但请注意,这种方式可能没有任何意义,因为价格值的实际原子修改是如此之快,以至于您不能期望从之前解锁地图中获得任何东西。

条件操作“检查它是否在映射中,创建一个新的,如果不在则插入”必须是原子的,并且应该通过在短时间内锁定整个映射来完成。其他任何事情都需要每个键都有一个专用的同步对象。这些必须在某处存储和管理,并且对该存储的访问必须再次同步等。

只需进行粗粒度锁定以确保正确性,然后继续。