我有一个访问缓存的泽西岛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 这样的缓存解决方案,而不是编写自己的解决方案。 即使缓存已关闭,您的服务也应正常工作。
如果您的服务一次只能由一个线程调用,那么您只需锁定服务即可!为什么要使用整数变量并锁定整数变量?
但是,在您的情况下,消息队列或类似的东西可能是一个优雅的解决方案。
你不需要实现它 - 你的策略是信号量的描述。您可以在 oracle 文档中查看信号量在维基百科中的工作方式以及 Java 中的类。