ExecutorService与ThreadPoolExecutor使用LinkedBlockingQueue的区别

64

我正在开发一个多线程项目,需要生成多个线程来测试我的客户端代码的端到端性能,因为我正在进行负载和性能测试。所以我编写了下面的代码,它使用ExecutorService

以下是使用ExecutorService的代码:

public class MultithreadingExample {

    public static void main(String[] args) throws InterruptedException {

        ExecutorService executor = Executors.newFixedThreadPool(20);
        for (int i = 0; i < 100; i++) {
            executor.submit(new NewTask());
        }

        executor.shutdown();
        executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
    }
}

class NewTask implements Runnable {

    @Override
    public void run() {
        //Measure the end to end latency of my client code
    }   
}

问题陈述:

我在浏览互联网上的一些文章时,发现还有ThreadPoolExecutor这个东西。所以我很困惑应该使用哪个。

如果我要将我的代码从下面这种形式进行替换:

ExecutorService executor = Executors.newFixedThreadPool(20);
    for (int i = 0; i < 100; i++) {
        executor.submit(new NewTask());
    }

至:

BlockingQueue<Runnable> threadPool = new LinkedBlockingQueue<Runnable>();

ThreadPoolExecutor tpExecutor = new ThreadPoolExecutor(20, 2000, 0L, TimeUnit.MILLISECONDS, threadPool);

tpExecutor.prestartAllCoreThreads();

    for (int i = 0; i < 100; i++) {
        tpExecutor.execute(new NewTask());
    }

这会有什么不同吗?我正在尝试理解使用ExecutorService的原始代码和新粘贴的使用ThreadPoolExecutor的代码之间的区别。我的一些团队成员说第二个(ThreadPoolExecutor)是正确的使用方式。

有人能为我澄清一下吗?

6个回答

92

这里是 Executors.newFixedThreadPool 方法的源码:

 public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

它在内部使用ThreadPoolExecutor类,并采用默认配置,正如您在上面所看到的。现在有些情况下默认配置不适用,比如说需要使用优先级队列而不是LinkedBlockingQueue等。在这种情况下,调用者可以通过实例化并将所需配置传递给ThreadPoolExecutor底层直接操作它。


32

那会有任何区别吗?

这会使你的代码更加复杂,收益微乎其微。

我正在尝试理解我的原始代码使用ExecutorService和我粘贴的新代码之间的区别,后者使用ThreadPoolExectuor?

几乎没有区别。 Executors 创建一个ThreadPoolExecutor来执行实际工作。

我的一些队友说第二个(ThreadPoolExecutor)是正确的使用方式?

仅仅因为它更加复杂,并不意味着它是正确的做法。设计者提供了Executors.newXxxx方法来使你的生活更简单,因为他们期望你使用这些方法。我建议你也使用它们。


你提到了一些小优势?这是什么意思?而在幕后,执行者被称为ThreadPoolExecutor对吧?但我相信它使用的是无界队列,这可能会稍微慢一点,不是吗?如果我错了请指出。 - user1813228
小小的好处是你可以玩弄构造函数参数,但这通常不是一个好主意。你提供了相同的无界队列,所以我不明白你的观点。如果它很慢,为什么设计者会使用它呢? - Peter Lawrey
使用LinkedBoundedQueue是有成本的。这与它是否无界无关,而且与在线程之间传递任务的成本相比非常小,我假设您对此并不太担心。 - Peter Lawrey
对我来说,这是一个重要的区别,因为我需要动态调整线程数。 - Jonathan Rosenne

14
  1. Executors#newFixedThreadPool(int nThreads)

    ExecutorService executor = Executors.newFixedThreadPool(20);
    

基本上是

 return new ThreadPoolExecutor(20, 20,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());

2.

BlockingQueue<Runnable> threadPool = new LinkedBlockingQueue<Runnable>();
ThreadPoolExecutor tpExecutor = new ThreadPoolExecutor(20, 2000, 0L,
    TimeUnit.MILLISECONDS, threadPool);

在第二种情况下,您只是将最大池大小增加到2000,我怀疑您不需要这么大的值。

4
我相信另一个优点是拒绝处理程序。如果我错了,请纠正我。

3
在第一个示例中,您使用以下语句仅创建了20个线程。
ExecutorService executor = Executors.newFixedThreadPool(20);

在第二个例子中,您已将线程限制范围设置在 20 到 2000 之间。
 ThreadPoolExecutor tpExecutor = new ThreadPoolExecutor(20, 2000, 0L, 
                                     TimeUnit.MILLISECONDS,threadPool);

有更多的线程可用于处理任务,但您已将任务队列配置为无限队列。

ThreadPoolExecutor 如果您定制了以下参数中的许多或所有参数,则会更加有效。

ThreadPoolExecutor(int corePoolSize, 
               int maximumPoolSize, 
               long keepAliveTime, 
               TimeUnit unit, 
               BlockingQueue<Runnable> workQueue, 
               ThreadFactory threadFactory,
               RejectedExecutionHandler handler)

拒绝执行处理程序在设置了workQueue最大容量并且已提交到Executor的任务数量超过workQueue容量时非常有用。

请查看ThreadPoolExecutor中的“被拒绝的任务”部分以获取更多详细信息。


1

经过两天的GC内存不足异常ThreadPoolExecutor拯救了我的生命。 :)

正如Balaji所说,

[...] 另一个优点是使用RejectionHandler。

在我的情况下,我有很多RejectedExecutionException,指定(如下)丢弃策略解决了我的所有问题。

private ThreadPoolExecutor executor = new ThreadPoolExecutor(1, cpus, 1, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadPoolExecutor.DiscardPolicy());

但要小心!它只在您不需要执行提交给执行器的所有线程时才起作用。

有关ThreadPoolExecutor的更多信息,请查看 Darren的答案


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接