Мне интересно, какой лучший подход к этой проблеме будет. У меня есть (абстрактно говоря) простой метод, который вызывает веб-сервис и сохраняет результат в локальном кэше в оперативной памяти, что-то вроде:Синхронизация одного конкретного вызова webservice
public Document callWebservice(SomeObject parameter) {
Document result = cache.get(parameter);
if (result == null) {
result = parse(retrieve(parameter));
cache.put(result);
}
return result;
}
Теперь, если документ находится в кэше, он может просто вернуться без проблемы, хорошо. В однопотоковой среде этот подход отлично работает. Однако в многопоточной среде получается, что каждый поток обращается к биту else, и вызывает веб-службу несколько раз.
Я мог бы вырезать синхронизированный блок в части «else», но я считаю, что это слишком «широкий» блокировки - весь метод будет недоступен для вызова потоков, хотя они называют совершенно разные вещи.
Это нормально, если веб-сервис вызывается дважды, если запрос отличается (т. Е. Параметр SomeObject, в данном случае).
Теперь вопрос: какой лучший подход принять в этом случае?
Я думал о сохранении параметра в объекте коллекции (threadafe). Если содержимое параметра одинаков, он будет вызывать тот же результат hashCode/equals и будет найден в объекте коллекции, указывая, что другой поток уже обрабатывает этот запрос. Если это будет так, вызывающий поток может быть приостановлен до тех пор, пока веб-служба не вернется. (Я должен был бы выяснить, как заставить вызывающий поток ждать, хотя). Будет ли это работать с блокировкой на объекте параметра SomeObject
? например:
private Map<SomeObject, SomeObject> currentlyProcessingItems = new ConcurrentHashMap<SomeObject, SomeObject>();
public Document callWebservice(SomeObject parameter) {
if (currentlyProcessedItems.contains(parameter)) {
parameter = currentlyProcessedItems.get(parameter);
} else {
currentlyProcessedItems.putIfAbsent(parameter);
}
synchronized(parameter) {
Document result = cache.get(parameter);
if (result == null) {
Document result = parse(retrieve(parameter));
cache.put(result);
}
currentlyProcessedItems.remove(parameter);
return result;
}
}
(примечание: логика для отслеживания в настоящее время обработки запросов, использование ConcurrentHashMap и замок может быть неоптимальным или откровенная неправильно)
Нет, я никогда не закончил читать книгу о многопоточности , Я должен.
Я уверен, что эта конкретная проблема довольно распространена, я просто не смог найти ответ. Что такое ситуация, подобная этой (например, блокировка на конкретном объекте), если я могу спросить?
У вас есть equals и hashCode прямо на SomeObject? –
Да, параметр имеет хороший 'hasCode()' и 'equals()' метод, основанный на Commons Lang's HashCodeBuilder и EqualsBuilder. Внутри он также имеет некоторые объекты, которые также правильно реализуют 'hashCode()' и 'equals()'. – fwielstra