具有限制/吞吐量控制的Java Executor

时间:2013-11-06 18:26:49

标签: java multithreading executors

我正在寻找允许我指定限制/吞吐量/起搏限制的Java Executor,例如,只需要在一秒钟内处理100个任务 - 如果更多任务被提交,他们应该排队等待后来执行。这样做的主要目的是避免在遇到外部API或服务器时遇到限制。

我想知道基础Java(我怀疑,因为我检查过)或其他可靠的地方(例如Apache Commons)是否提供了这个,或者我是否必须编写自己的。最好是轻量级的。我不介意自己写,但如果有一个“标准”版本,那么我至少要先看看它。

6 个答案:

答案 0 :(得分:25)

看看guavas RateLimiter

  

速率限制器。从概念上讲,限速器在a处分发许可证   可配置的速率。每个acquire()都会在必要时阻止许可   是可用的,然后接受它。一旦获得,许可证不必   释放。速率限制器通常用于限制速率   访问某些物理或逻辑资源。这与之形成鲜明对比   信号量限制并发访问的数量而不是   速率(注意虽然并发和速率密切相关,   例如见小法则。

它的线程安全,但仍然是@Beta。无论如何,可能值得一试。

对于速率限制器,您必须将每个调用包装到Executor。对于更干净的解决方案,您可以为ExecutorService创建某种包装器。

来自javadoc:

 final RateLimiter rateLimiter = RateLimiter.create(2.0); // rate is "2 permits per second"
  void submitTasks(List<Runnable> tasks, Executor executor) {
    for (Runnable task : tasks) {
      rateLimiter.acquire(); // may wait
      executor.execute(task);
    }
  }

答案 1 :(得分:6)

Java Executor没有提供这样的限制,只是受到线程数量的限制,这不是你想要的。

一般情况下,Executor是限制此类操作的错误位置,它应该是Thread尝试调用外部服务器的时刻。例如,您可以通过让线程在提交请求之前等待的限制Semaphore来执行此操作。

调用线程:

public void run() {
  // ...
  requestLimiter.acquire();
  connection.send();
  // ...
 }

同时您定期(例如每60秒)安排一个(单个)辅助线程释放获取的资源:

 public void run() {
  // ...
  requestLimiter.drainPermits();  // make sure not more than max are released by draining the Semaphore empty
  requestLimiter.release(MAX_NUM_REQUESTS);
  // ...
 }

答案 2 :(得分:3)

  

只需说一秒就可以处理100个任务 - 如果更多的话   任务被提交,他们应该排队并稍后执行

您需要查看Executors.newFixedThreadPool(int limit)。这将允许您限制可以同时执行的线程数。如果您提交多个线程,它们将排队并稍后执行。

ExecutorService threadPool = Executors.newFixedThreadPool(100);
Future<?> result1 =  threadPool.submit(runnable1);
Future<?> result2 = threadPool.submit(runnable2);
Futurte<SomeClass> result3 = threadPool.submit(callable1);  
...  

上面的代码段显示了如何使用允许同时执行不超过100个线程的ExecutorService

<强>更新
在回顾评论之后,这就是我提出的(有点愚蠢)。如何手动保持要执行的线程的跟踪?如何先将它们存储在ArrayList中,然后根据最后一秒内已执行的线程数将它们提交给Executor
因此,我们可以说已经将200个任务提交到我们维护的ArrayList中,我们可以迭代并向Executor添加100个任务。当第二个通过时,我们可以根据Executor中已完成的数量添加更多线程,依此类推

答案 3 :(得分:1)

根据场景,以及之前的一个回复中的建议,ThreadPoolExecutor的基本功能可以解决问题。

但是如果线程池由多个客户端共享并且您想要限制它们以限制每个客户端的使用,确保一个客户端不会使用所有线程,那么BoundedExecutor将完成工作。 / p>

可以在以下示例中找到更多详细信息:

http://jcip.net/listings/BoundedExecutor.java

答案 4 :(得分:0)

我个人觉得这个场景非常有趣。在我的例子中,我想强调一个有趣的节流阶段是消费方面,如经典的生产者/消费者并发理论。这与之前的一些建议答案相反。这是,我们不想阻止提交线程,但是基于速率(任务/秒)策略阻止消费线程。因此,即使队列中有任务准备就绪,执行/消耗线程也可能阻止等待满足节点策略。

那就是说,我认为一个好的候选人将是Executors.newScheduledThreadPool(int corePoolSize)。这样,您需要在执行程序前面有一个简单的队列(一个简单的LinkedBlockingQueue适合),然后安排一个定期任务从队列中选择实际任务(ScheduledExecutorService.scheduleAtFixedRate)。所以,这不是一个简单的解决方案,但如果你试图像前面讨论的那样限制消费者,那么它应该足够好。

答案 5 :(得分:0)

可以将其限制在Runnable内:

public static Runnable throttle (Runnable realRunner, long delay) {
    Runnable throttleRunner = new Runnable() {
        // whether is waiting to run
        private boolean _isWaiting = false;
        // target time to run realRunner
        private long _timeToRun;
        // specified delay time to wait
        private long _delay = delay;
        // Runnable that has the real task to run
        private Runnable _realRunner = realRunner;
        @Override
        public void run() {
            // current time
            long now;
            synchronized (this) {
                // another thread is waiting, skip
                if (_isWaiting) return;
                now = System.currentTimeMillis();
                // update time to run
                // do not update it each time since
                // you do not want to postpone it unlimited
                _timeToRun = now+_delay;
                // set waiting status
                _isWaiting = true;
            }
            try {
                Thread.sleep(_timeToRun-now);

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // clear waiting status before run
                _isWaiting = false;
                // do the real task
                _realRunner.run();
            }
        }};
    return throttleRunner;
}

取自JAVA Thread Debounce and Throttle