ThreadPoolExecutor不执行所有任务

时间:2017-08-31 11:40:52

标签: java multithreading

我有一个ThreadPoolExecutor - corePoolSize = 5,maxPoolSize = 10 queueSize = 10,keepAlive = 1000秒。我正在执行int i1 = xml.IndexOf("<LabelImage>") + "<LabelImage>".Length; int i2 = xml.IndexOf("</LabelImage>"); Byte[] vbf = Convert.FromBase64String(xml.Substring(i1,i2-i1)); 个任务。实际执行的任务数量是变化的,并非所有任务都被执行。 RejectionHandler也没有报告任何内容。我相信我对ThreadPoolExecutor的理解是错误的。有谁能够帮我?如何执行所有任务?

XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(xml);
var imageBase64  = xmldoc.GetElementsByTagName("LabelImage").Item(0).InnerText;
Byte[] image= Convert.FromBase64String(imageBase64);

示例输出

100 Runnable

4 个答案:

答案 0 :(得分:3)

假设100个线程不能被处理为maxPoolSize = 10和queueSize = 10,这意味着你可以在最坏的情况下放入你的拉执行器只有20个线程。最佳情况可能会根据每个线程内的作业的性能和复杂性而改变。尝试将queueSize增加到90.所以肯定有90个会等待,其他10个将继续工作。 您可以在link找到最佳的表达方式:

  

如果请求无法排队,则会创建一个新线程,除非这样   会超过maximumPoolSize,在这种情况下,任务将是   拒绝。

答案 1 :(得分:3)

我很欣赏 @Speise 提供的答案,但只是为了增加一些清晰而明显的细节;我会引用oracle文档中提到的声明:

  

如果正在运行少于corePoolSize的线程,则Executor总是更喜欢添加新线程而不是排队。

     

如果corePoolSize或更多线程正在运行,则Executor总是更喜欢排队请求而不是添加新线程。

     

如果请求无法排队,则会创建一个新线程,除非这会超过maximumPoolSize,在这种情况下,该任务将被拒绝。

文档非常清晰,并指出为了避免拒绝任务,您应该增加maxPoolSize。而不是增加Queue大小,因为可能没有案例(如当你事先知道要提交多少任务时(你可以调整maxPoolSize)。我还建议你在这种类型的场景中使用UnboundedQueues(LinkedBlockingQueue)(但是,对于这种情况可能有一个快速修复,你也可以增加将请求推送到队列的时间)。

同样增加队列大小会妨碍性能,所以为什么不将它(相应地创建线程)留给JVM。(因为ThreadPool根据maxPoolSize采取行动)

答案 2 :(得分:2)

TestThreadPoolExecutor的参数:

corePoolSize - 池中保留的线程数,即使它们处于空闲状态。

maximumPoolSize - 池中允许的最大线程数。

keepAliveTime - 当线程数大于核心时,这是多余空闲线程在终止之前等待新任务的最长时间。

unit - keepAliveTime参数的时间单位。

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

handler - 执行被阻止时使用的处理程序,因为已达到线程边界和队列容量。

您的情景: corePoolSize = 5,maximumPoolSize = 10,workqueue = 10

你正在推进100个任务for for循环。

首先,你永远不会有超过10个工作线程。

假设您的所有工作线程都忙,那么大多数工作队列可以保持10个。之后,任何更多的任务提交都将被拒绝,您可以在拒绝处理程序中捕获该拒绝。 因此,增加工作队列将有助于解决问题。 (但不推荐)

你可以尝试:

在推动工作时使用一些时间差距,而不是使用for循环,这实际上是在同一个实例中推动工作。

将maximumPoolSize设置为核心X 2(或4)的数量。

当您的工作被拒绝时,您可以尝试在延迟一段时间后重新提交工作

PS:这是生产者 - 消费者问题。它总是要求消费者比生产者更快。在某些时刻,生产者变得更快,我们看到工作负荷飙升。为了处理这种负载峰值,设计了队列,但即使队列溢出,拒绝也会被发送回生产者,生产者有责任通过重新提交或丢弃来优雅地处理它。

答案 3 :(得分:1)

感谢@ Mandy8055,@ Ritesh,@ Speise

如果无法预测queueSize,我找到了两种方法,可以优雅地处理这个问题。

  1. 编写拒绝处理程序,将任务提交回队列。但是,正如@Ritesh指出的那样,生产者可能比消费者更快,我们可能最终获得StackOverFlow。 无论如何,这就是我所做的:

    class RejectedExecutionHandlerImpl implements RejectedExecutionHandler {
    
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
    DummyRunnableTask task =  (DummyRunnableTask)r;
    
    System.out.println("Task id "+task.getI()+" got rejected, resubmitting");
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    executor.execute(r);
    }
    }
    

    现在,这并不是处理它的好方法,因为你只是不知道,如果任务需要100毫秒才能完成。也许我们可以创建一个被拒绝的任务列表,然后重新提交它们。

  2. 另一种方法是在达到某个限制(maxPoolSize + queueSize)时阻止提交/执行方法ThreadPoolExecutor并使其等待直到有空格。我们通过在ThreadPoolExecutor的自定义实现中使用Bounded Semaphore来实现这一点

    class CustomExecutor { 
    
    private final ThreadPoolExecutor executor;
    private final Semaphore semaphore;
    
    public CustomExecutor(int corePoolSize, int maxPoolSize, int keepAliveTime, TimeUnit unit,int queueSize, ThreadFactory factory) {
    BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(queueSize);
    this.executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, queue, factory);
    this.semaphore = new Semaphore(queueSize + maxPoolSize);
    }
    public void shutDown(){
    this.executor.shutdown();
    }
    
    public ThreadPoolExecutor getExecutor() {
    return executor;
    }
    
    
    private void exec(final Runnable command) throws InterruptedException {
    semaphore.acquire();
    try {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    command.run();
                } finally {
                    semaphore.release();
                }
            }
        });
    } catch (RejectedExecutionException e) {
        semaphore.release();
        throw e;
    }
    }
    
    public void execute (Runnable command) throws InterruptedException {
    exec(command);
      }
    }
    
  3. 如果您能想到其他任何方式,请告诉我们。