Java throttle 机制

标签 java multithreading locking mutex throttling

更新:我使用的是 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) 方法可以调用也可以不调用。 (当然它总是可以被调用,但是如果我们超过了我们的速率就没有调用它的意义了。)

所以可以让我像这样编写代码:

// 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),但我仍然需要一个可靠的机制来暂停/hibernate 直到 WidgetService#doSomething(Fizz) 可以被调用。

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

最佳答案

最好的选择之一可能是使用信号量 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() 来执行对服务的调用时都会消耗许可,如果所有许可都已耗尽,这将阻塞。

正如您提到的,当然可以使用带有计时器或单独线程的等待/通知和整数计数器来对此进行编码,但是与使用更现代的 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 开始工作。

更好的解决方案是使用 Guava 的 com.google.common.util.concurrent.RateLimiter。它可能看起来像这样:

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

  makeCall() {
    rateLimiter.acquire();
    doMakeCall();
  }
}

与 Semaphore 解决方案相比,语义略有不同,RateLimiter 很可能更适合您的情况。

关于Java throttle 机制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14638349/

相关文章:

java - 尝试访问 JNA 函数时出现 IllegalArgumentException

java - 如何通知第二个线程变量的更改

c - 如何在 C pthreads 中并行化线程

c++ - std::map 是否锁定其节点以防止其他进程删除它们?

java - Spring 部分更新对象数据绑定(bind)

java - 如何通过双击来编辑 JList 项目文本?

api - 如何在 Google map 中锁定国家/地区

c++ - shared_mutex 锁排序

java - Buffer Reader 俄语字符编码字符集

c# - 从另一个类 c# 更新 gui