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)
怎么样
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)
但请注意,这种方式可能没有任何意义,因为价格值的实际原子修改是如此之快,以至于您不能期望从之前解锁地图中获得任何东西。
条件操作“检查它是否在映射中,创建一个新的,如果不在则插入”必须是原子的,并且应该通过在短时间内锁定整个映射来完成。其他任何事情都需要每个键都有一个专用的同步对象。这些必须在某处存储和管理,并且对该存储的访问必须再次同步等。
只需进行粗粒度锁定以确保正确性,然后继续。