Есть писатель, который обновляет цены, вызывая метод 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;
}
}
}
Цена.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;
}
Thread.sleep(3000);
в синхронизированном блоке, так как нет смысла ждать потока на таком критическом пути. - person izce   schedule 03.11.2015prices.put(e, currentPrice);
в методеgetPrice(...)
, поскольку у вас уже есть ссылка на тот же объектPrice
вcurrentPrice
variable. - person izce   schedule 03.11.2015getPrice()
, где я обновляю свойствоhasChangedSilnceLastRead
? Например, если поток 1 прочитал цену A, поток 2 изменил цену A, а затем я снова переопределяю последний поток 1 - несоответствие? - person Wild Goat   schedule 03.11.2015getPrice()
, где я выполняю несколько операций: чтение и обновление, может ли это вызвать несогласованность, если другой поток будет писать в между этими операциями? - person Wild Goat   schedule 03.11.2015synchronized (prices.get(e)) {
- Что делать, если карта изначально пуста, т.е.get()
возвращаетnull
? - person JimmyB   schedule 03.11.2015I think that locking the whole map may cause a performance issue
, мыслить хорошо, но в таких случаях этого недостаточно. Вам также необходимо получить данные. - person biziclop   schedule 03.11.2015