4

I have wrote code sample:

class Test {
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor executorService = new ThreadPoolExecutor(0, 100,
                2L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>());
        executorService.allowCoreThreadTimeOut(true);

        CountDownLatch countDownLatch = new CountDownLatch(20);
        long l = System.currentTimeMillis();
        for (int i = 0; i < 20; i++) {
            Thread.sleep(100);
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        countDownLatch.countDown();
                        Thread.sleep(500);
                    } catch (Exception e) {
                        System.out.println(e);
                    }


                }
            });
        }
        executorService.shutdown();
        countDownLatch.await();

        System.out.println((System.currentTimeMillis() - l) / 100);
    }
}

Each 100 ms submits new task(overall task quantity - 20). Each task duration - 0.5 sec. thus 5 task can be executed in parallel and best execution time will be: 20*100+500 = 2.5 sec and pool should create 5 threads

enter image description here

But my experiment shows 9.6 sec.
I opened the jsvisualvm to see how many threads pool creates and I see that only one thread was created:

enter image description here

please correct where my threadPooll configuration incorrect.

gstackoverflow
  • 36,709
  • 117
  • 359
  • 710
  • It seems like we are making up a great team today ;-) ... more questions to come from your side? And just out of curiosity: last time you upvoted and accepted; this time just an accept; is there a deeper logic behind that? – GhostCat Feb 17 '17 at 13:00
  • I don't inderstand why if I use SynchronousQueue in my example - only one thread uses but in case of newCachedThreadPool(it uses SynchrounousQueue inside) - thread count differs than 1 – gstackoverflow Feb 17 '17 at 13:07
  • For that part, one would probably have to look exactly into the source code. Maybe the "factory" provided code uses some slightly different queue class; some kind of subtle difference there. – GhostCat Feb 17 '17 at 13:13
  • Oops,I was wrong! Synchonous queue always full – gstackoverflow Feb 17 '17 at 13:39

2 Answers2

4

The reason can be found from the ThreadPoolExecutor javadoc.

Any BlockingQueue may be used to transfer and hold submitted tasks.

The use of this queue interacts with pool sizing: If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing.

If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread.

If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected.

Therefore since your corePoolSize is 0, the first option is not used and since your queue is unlimited, the last option is not used. Therefore the remaining strategy of queuing is what you get.

Different results can be seen by modifying corePoolSize or the size of the workQueue.

Community
  • 1
  • 1
Kayaman
  • 72,141
  • 5
  • 83
  • 121
  • what about SynchronousQueue? – gstackoverflow Feb 17 '17 at 12:10
  • @gstackoverflow What about it? You wouldn't want to use a 0 sized queue for a workQueue. – Kayaman Feb 17 '17 at 12:12
  • my additional qaestion why if I use SynchronousQueue - I see same bad result and I don't know about can be queued task to this queue or not. Indeed after replace queue to SynchronousQueue - I see old result(9.6) – gstackoverflow Feb 17 '17 at 12:19
  • Because a `SynchronizedQueue` isn't really a queue per se. It's more related to the `Exchanger` class in functionality. Its blocking behaviour makes it unsuitable to be used here. If you want to actually see the location where `SyncronousQueue` blocks, go through the source code for `ThreadPoolExecutor` and find out. – Kayaman Feb 17 '17 at 12:34
  • @gstackoverflow: I don’t know what you are doing wrong, `SynchronousQueue` works perfectly for me, completing your example after 2 sec (you count down the latch *before* `sleep(500)`). By the way, `SynchronousQueue` is exactly what [`Executors.newCachedThreadPool()`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newCachedThreadPool--) uses together with an unlimited number of threads. Having no thread limit ensures that there will never be a `RejectedExecutionException`, instead, it will always start a new thread, if there is no old thread accepting the task. – Holger Feb 23 '17 at 17:51
0

I guess the answer to this behavior could be rooted in:

A ThreadPoolExecutor will automatically adjust the pool size (see getPoolSize()) according to the bounds set by corePoolSize (see getCorePoolSize()) and maximumPoolSize (see getMaximumPoolSize()). When a new task is submitted in method execute(java.lang.Runnable), and fewer than corePoolSize threads are running, a new thread is created to handle the request, even if other worker threads are idle. If there are more than corePoolSize but less than maximumPoolSize threads running, a new thread will be created only if the queue is full.

( from ThreadPoolExecutor javadoc ).

Thing is: how are sleeping threads coming into this equation. My suggestion: change the corePoolSize from 0 to 10; and set the max pool size to 10, too.

GhostCat
  • 137,827
  • 25
  • 176
  • 248