在ThreadPoolExecutor中提交(Callable <t>任务)方法</t>

时间:2015-02-22 15:47:04

标签: java multithreading executorservice threadpoolexecutor

ThreadPoolExecutor继承了submit(Callable<T> task)方法 ThreadPoolExecutor的构造函数接受实例BlockingQueue<Runnable>。此阻止队列只能保留Runnable个实例 ThreadPoolExecutor构造函数的Javadoc说:

  

用于在执行任务之前保留任务的队列。此队列将仅保存execute方法提交的Runnable任务。

所以,我的问题是:通过submit(Callable<T> task)提交的任务如何排队?

4 个答案:

答案 0 :(得分:0)

使用Runnable将其包裹在RunnableFuture(特别是newTaskFor(Callable))中。请参阅source code

答案 1 :(得分:0)

您可以在queuing section of the ThreadPoolExecutor documentation中找到解释:

  

<强>队列

     

任何BlockingQueue都可用于转移和保留提交的任务。此队列的使用与池大小调整相互作用:

     
      
  • 如果运行的corePoolSize线程少于,则执行程序总是更喜欢添加新线程而不是排队。
  •   
  • 如果corePoolSize或更多线程正在运行,则Executor总是更喜欢排队请求而不是添加新线程。
  •   
  • 如果请求无法排队,则会创建一个新线程,除非这会超过maximumPoolSize,在这种情况下,该任务将被拒绝。
  •   
     

排队有三种常规策略:

     
      
  1. 直接切换。工作队列的一个很好的默认选择是SynchronousQueue,它将任务交给线程而不另外保存它们。在这里,如果没有线程立即可用于运行它,则尝试对任务进行排队将失败,因此将构造新线程。此策略在处理可能具有内部依赖性的请求集时避免了锁定。直接切换通常需要无限制的maximumPoolSizes以避免拒绝新提交的任务。这反过来承认,当命令继续平均到达的速度超过可以处理的速度时,无限制线程增长的可能性。

  2.   
  3. 无界队列。使用无界队列(例如,没有预定义容量的LinkedBlockingQueue)将导致新任务在所有corePoolSize线程忙时在队列中等待。因此,只会创建corePoolSize线程。 (并且maximumPoolSize的值因此没有任何影响。)当每个任务完全独立于其他任务时,这可能是适当的,因此任务不会影响彼此的执行;例如,在网页服务器中。虽然这种排队方式可以有助于平滑瞬态突发请求,但它承认,当命令继续平均到达的速度超过可以处理的速度时,无限制的工作队列增长的可能性。

  4.   
  5. 有界队列。有限队列(例如,ArrayBlockingQueue)在与有限maximumPoolSizes一起使用时有助于防止资源耗尽,但可能更难以调整和控制。队列大小和最大池大小可以相互交换:使用大型队列和小型池最小化CPU使用率,OS资源和上下文切换开销,但可能导致人为的低吞吐量。如果任务经常阻塞(例如,如果它们是I / O绑定的),系统可能能够为您提供比您允许的更多线程的时间。使用小队列通常需要更大的池大小,这会使CPU更加繁忙,但可能会遇到不可接受的调度开销,这也会降低吞吐量。

  6.   

可以在Executors类中找到一些示例,它提供了创建多种ThreadPoolExecutor类型的方法。

答案 2 :(得分:0)

使用带有提交(Callable)的有界队列是一件痛苦的事情,因为即使使用BlockingQueue也没有默认的方法来阻止它(我知道)。

可以帮助的一件事就是从提交中捕获RejectedExecutionException,然后等待再试一次。如果您的应用程序应阻止提交,直到阻塞队列中的容量可用,这将是合适的。例如,换行:

 futures.add(executor.submit(callable));
像这样:

boolean submitted = false;
while(!submitted) {
  try {
    futures.add(executor.submit(callable));
    submitted = true;
  } catch(RejectedExecutionException e) {
    try {                       
       Thread.sleep(1000);
    } catch(InterruptedException e2) {
       throw new RuntimeException("Interrupted", e2);
    }
  }
}

我希望有所帮助。

答案 3 :(得分:0)

不需要任何包装。您可以通过 Executors 或直接作为新的 ThreadPoolExecutor 来创建 ExecutorService 。使用 submit(Callable) 方法,ThreadPoolExceutor 将为您包装 Callable 和 Runnable。 请注意,如果您使用拒绝处理程序,请记住您将获得包含您的 Callable 的包装器对象 FutureTask

@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor){
   FutureTask ft= (FutureTask)r;
}