ExecutorService vs ThreadPoolExecutor using LinkedBlockingQueue
Asked Answered
L

6

69

I am working on a multithreaded project in which I need to spawn multiple threads to measure the end to end performance of my client code, as I'm doing Load and Performance testing. So I created the below code which is using ExecutorService.

Below is the code with 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
    }   
}

Problem statement:

Now I was reading some article on the Internet. I found out there is ThreadPoolExecutor as well. So I got confused which one I should be using.

If I replace my above code from:

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

to:

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());
    }

will this make any difference? I am trying to understand what is the difference between my original code using ExecutorService and the new code pasted using ThreadPoolExecutor. Some of my team mates said second one (ThreadPoolExecutor) is the right way to use.

Can anyone clarify this for me?

Libenson answered 23/4, 2013 at 5:53 Comment(0)
L
94

Here is the source of Executors.newFixedThreadPool:

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

It internally uses ThreadPoolExecutor class with default configuration as you can see above. Now there are scenarios where default configuration is not suitable say instead of LinkedBlockingQueue a priority queue needs to be used etc. In such cases caller can directly work on underlying ThreadPoolExecutor by instantiating it and passing desired configuration to it.

Larrylars answered 23/4, 2013 at 6:2 Comment(0)
K
34

then that will make any difference?

It will make your code more complicated for little benefit.

I am trying to understand what is the difference between my original code which is using ExecutorService and the new code, that I pasted which is using ThreadPoolExectuor?

Next to nothing. Executors creates a ThreadPoolExecutor to do the real work.

Some of my team mates said second one (ThreadPoolExecutor) is right way to use?

Just because it's more complicated doesn't mean it's the right thing to do. The designers provided the Executors.newXxxx methods to make life simpler for you and because they expected you to use those methods. I suggest you use them as well.

Kif answered 23/4, 2013 at 5:59 Comment(4)
You talked about some little benefit? What is that all about? And behind the scene Executors called ThreadPoolExecutor right? But I believe it uses Unbounded queue which is slightly slow I guess? Correct me if I am wrong.Libenson
The little benefit is you can play with the constructor arguments, but this is usually not a good idea. You provided the same unbounded queue, so I don't see your point. If it was slow, why would the designers use it?Kif
There is a cost in using LinkedBoundedQueue. It has nothing to do with the fact it is unbounded and it is very small compared to the cost of passing tasks between threads, which I assume you are not too worried about.Kif
For me there is an important difference, because I need to adjust the number of threads dynamically.Bacteriophage
R
15
  1. Executors#newFixedThreadPool(int nThreads)

    ExecutorService executor = Executors.newFixedThreadPool(20);
    

is basically

 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);

In the second case, you are just increasing the maxPoolSize to 2000, which I doubt you would need.

Ri answered 23/4, 2013 at 6:1 Comment(0)
H
4

I believe one more advantage is with RejectionHandler. Correct me if wrong

Hackberry answered 21/4, 2014 at 22:40 Comment(0)
L
3

In first example, You have created just 20 threads with below statement

ExecutorService executor = Executors.newFixedThreadPool(20);

In second example, you have set the thread limits range in between 20 to 2000

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

More threads are available for processing. But you have configured task queue as unbounded queue.

ThreadPoolExecutor would be more effective if you have customized many or all of below parameters.

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

RejectedExecutionHandler would be useful when you set max capacity for workQueue and number of tasks, which have been submitted to Executor are more than workQueue capacity.

Have a look at Rejected tasks section in ThreadPoolExecutor for more details.

Linin answered 19/1, 2016 at 17:32 Comment(0)
S
1

After 2 days of GC out of memory exception, ThreadPoolExecutor saved my life. :)

As Balaji said,

[..] one more advantage is with RejectionHandler.

In my case I had a lot of RejectedExecutionException and specifying (as follow) the discard policy solved all my problems.

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

But be careful! It works only if you don't need to execute all the threads that you submit to the executor.

For further information about ThreadPoolExecutor take a look at Darren's answer

Stern answered 10/11, 2015 at 10:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.