Update:我使用的是 Java 1.6.34,没有机会升级到 Java 7。
我有一个场景,每分钟只允许调用一个方法 80 次。它实际上是由第 3 方编写的服务 API,如果调用次数过多,它会“关闭”(忽略调用)其 API:
public class WidgetService {
// Can only call this method 80x/min, otherwise it
// it just doesn't do anything
public void doSomething(Fizz fizz);
}
我想写一个ApiThrottler
类有一个boolean canRun()
方法将告诉我的 Java 客户端是否doSomething(Fizz)
方法可以被调用,也可以不被调用。 (当然,它总是可以called,但如果我们超出了我们的费率,就没有调用它的意义。)
所以可以让我编写这样的代码:
// 80x/min
ApiThrottler throttler = new ApiThrottler(80);
WidgetService widgetService = new DefaultWidgetService();
// Massive list of Fizzes
List<Fizz> fizzes = getFizzes();
for(Fizz fizz : fizzes)
if(throttler.canRun())
widgetService.doSomething(fizz);
这不一定是 API (ApiThrottler#canRun
),但尽管如此,我需要一个坚实/可靠的机制,它将暂停/睡眠,直到WidgetService#doSomething(Fizz)
can叫做。
这让我feel就像我们正在进入使用多线程的领域一样,这让我feel就像我们可以使用某种锁定机制和 Java 通知(wait()
/notify()
) 模型。但由于没有这方面的经验,我似乎无法理解最优雅的解决方案。提前致谢。
最好的选择之一可能是使用信号量http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html类并每分钟给予 80 个许可。例如,这可以通过使用计时器类来完成http://docs.oracle.com/javase/7/docs/api/java/util/Timer.html http://docs.oracle.com/javase/7/docs/api/java/util/Timer.html。
调用者线程每次通过在信号量上调用 acquire() 来执行对服务的调用时都会消耗许可证,如果所有许可证都已耗尽,则会阻塞。
当然,正如您所提到的,可以使用等待/通知和带有计时器或单独线程的整数计数器来对此进行编码,但这与我概述的更现代的 java.util.concurrent API 的使用相比会更复杂多于。
它看起来接近于以下内容:
class Throttler implements TimerTask {
final Semaphore s = new Semaphore(80);
final Timer timer = new Timer(true);
Throttler() {
timer.schedule(this, 0, 60*1000); //schedule this for 1 min execution
}
run() { //called by timer
s.release(80 - s.availablePermits());
}
makeCall() {
s.acquire();
doMakeCall();
}
}
这应该从 Java 5 开始工作。
另外更好的解决方案是使用com.google.common.util.concurrent.RateLimiter
来自番石榴。它可以看起来像这样:
class Throttler {
final RateLimiter rateLimiter = RateLimiter.create(80.0/60.0);
makeCall() {
rateLimiter.acquire();
doMakeCall();
}
}
与 Semaphore 解决方案相比,语义略有不同,RateLimiter 很可能更适合您的情况。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)