Есть писатель, который обновляет цены, вызывая метод putPrice
. Читатель использует getPrice
, чтобы получить последнюю цену. hasChangedMethod
возвращает логическую идентификацию, если цена была изменена с последнего вызова getPrice
.Java-совместимые замки на уровне ключа карты
Я ищу быстрое решение. Я пытаюсь достичь потокобезопасного последовательного чтения/записи на карте на ключевом уровне.
Я думаю, что блокировка всей карты может вызвать проблему с производительностью, поэтому я решил сделать ее на ключевом уровне. К сожалению, он не работает должным образом и блокирует всю карту. Зачем? Не могли бы вы помочь мне разобраться, что я здесь делаю неправильно?
UPDATE:
Я думаю, мы можем суммировать два вопроса: 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;
}
}
}
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;
}
Вы знаете заранее, что ключи (я имею в виду названия валюты, такие как евро и доллары США) будут находиться в режиме разработки или писатель может также поставить некоторые новые валюты во время выбега время? – IzCe
@IzCe, да, я знаю все ключи заранее. В основном количество валют в мире. Может быть, я немного смущен, тогда как бы я заблокировал его на ключевом уровне? –
Поскольку вы знаете ключи заранее, вам не нужно использовать ConcurrentMap. Достаточно просто Карта. Во время инициализации вашей программы основной поток может заполнить карту этими клавишами и объектами Price со значениями по умолчанию. Все, что вам нужно - это получить объект Price и синхронизировать его для каждой операции get и put. Вам не нужно использовать 'Thread.sleep (3000);' вызов в синхронизированном блоке, так как нет смысла ждать потока в таком критическом пути. – IzCe