У меня есть метод службы REST Джерси, который обращается к кешу. Этот метод отслеживает requestsBeingProcessed
, поскольку MessageService
периодически обновляет кэш, но может сделать это только, когда нет никаких запросов, обрабатываемый. Синхронизация вызовов для увеличения и уменьшения количества обрабатываемых запросов выполняется для обеспечения потокобезопасного доступа.
class TeamInfoService {
@GET
@Path("/{teamId}")
@Produces(MediaType.TEXT_PLAIN)
public String getTeamInfo(@PathParam("teamId") final int teamId) {
MessageService.incrementRequestsBeingProcessed;
String team = teamCache.getTeams().get(teamId);
MessageService.decrementRequestsBeingProcessed;
return team;
}
}
class MessageService {
private static int requestsBeingProcessed = 0;
public synchronized static void incrementRequestsBeingProcessed() {
requestsBeingProcessed++;
}
public synchronized static void decrementRequestsBeingProcessed() {
requestsBeingProcessed--;
}
public synchronized static void getRequestsBeingProcessed() {
return requestsBeingProcessed;
}
}
Проблема заключается в том, что MessageService
должен получить блокировку для обновления кеша, но может обновлять ее только путем проверки requestsBeingProcessed
, к которым можно получить доступ только по одному потоку за раз.
public synchronized static void updateCache(String message) {
while(getRequestsBeingProcessed() != 0) {
//wait until there are no requests being processed
}
processMessage(message);
}
У меня здесь ситуация с курицей/яйцом: я не могу заставить блокировку обновлять requestsBeingProcessed
потому что требуется блокировка для проверки requestsBeingProcessed
. Есть ли другой способ, которым я должен приближаться к этой проблеме?
Вы должны уведомлять об обновлении нити, если в очереди нет элементов:
public synchronized static void decrementRequestsBeingProcessed() {
if (requestsBeingProcessed > 0) requestsBeingProcessed--;
if (requestsBeingProcessed == 0) MessageService.class.notifyAll();
}
public synchronized static void updateCache(String message) {
try {
while(getRequestsBeingProcessed() != 0) {
MessageService.class.wait();
}
processMessage(message);
} catch (InterruptedException ie) {
// devise cancellation strategy here...
}
}
Я бы посоветовал против такого подхода. Поскольку вы обнаруживаете, писать многопоточный код трудно даже для умных людей.
Другой подход, который вы могли бы попробовать, - это соглашение производителя/потребителя с блокирующим детексом. Встраивается требуемая резьба; вам не придется иметь дело с этим.
Я бы также рассмотрел такое кэширующее решение, как JCS, вместо того, чтобы писать ваши собственные. Ваш сервис должен работать, даже если кеширование отключено.
Если ваша услуга может быть вызвана только одним потоком за раз, то вы просто ЗАБРОНИРУЕТ СЕРВИС! Почему вы используете целочисленную переменную и блокируете целочисленную переменную?
Однако, в вашем случае, может быть, очередь сообщений или что-то в этом роде является изящным решением.
Вам не нужно его реализовывать - ваша тактика - это описание семафора. Вы можете видеть, как семафоры работают в Википедии и классе на Java в документации оракула.
requestsBeingProcessed
достигнет 0, но другой поток должен получить блокировку, чтобы обновить requestsBeingProcessed
.
Object.wait
иObject.notify
/Object.notifyAll
. ВdecrementRequestsBeingProcessed
, когда вы обнаруживаете, что requestBeingProcessed достигает нуля, вы можете вызватьnotify
. Это разблокирует синхронизированный монитор во время выполненияnotify
- проверьте Javadoc.