Java thottling机制

IAm*_*aja 3 java multithreading mutex throttling locking

更新:我在Java 1.6.34上,没有机会升级到Java 7.

我有一个场景,我只允许每分钟调用一次方法80次.它实际上是由第三方编写的服务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);
}
Run Code Online (Sandbox Code Playgroud)

我想写一个ApiThrottler类,它有一个boolean canRun()方法可以告诉我的Java客户端是否doSomething(Fizz)可以调用该方法.(当然它总是被称为,但如果我们超过了我们的费率,就没有任何意义.)

所以我可以编写如下代码:

// 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);
Run Code Online (Sandbox Code Playgroud)

这不一定是API(ApiThrottler#canRun),但是我需要一个可靠/可靠的机制,它可以暂停/休眠直到WidgetService#doSomething(Fizz) 可以被调用.

这让我觉得我们正在进入使用多线程的领域,这让我觉得我们可以使用某种锁定机制和Java通知(wait()/ notify())模型.但是在这个领域没有经验,我似乎无法绕过最优雅的解决方案.提前致谢.

hgr*_*rey 5

可能最好的选择之一是使用Semaphore 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来完成.每次调用者线程通过调用信号量上的acquire()来执行对服务的调用时,它将使用许可证,如果所有许可证已经耗尽,它将阻塞.

当然可以使用wait/notify和带有计时器或单独线程的整数计数器对其进行编码,如你所提到的那样,但与使用我已概述的更现代的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();
  }

}
Run Code Online (Sandbox Code Playgroud)

这应该从Java 5开始.

甚至更好的解决方案是使用com.google.common.util.concurrent.RateLimiterGuava.它看起来像这样:

class Throttler {
  final RateLimiter rateLimiter = RateLimiter.create(80.0/60.0);

  makeCall() {
    rateLimiter.acquire();
    doMakeCall();
  }
}
Run Code Online (Sandbox Code Playgroud)

与Semaphore解决方案相比,语义略有不同,RateLimiter最适合您的情况.

  • 而不是试图排空所有许可证,然后释放80,你可能只想释放`80 - s.availablePermits()`并避免竞争许可证. (2认同)