和朱曄一起復習Java併發(一):執行緒池

powerzhuye發表於2019-07-13

和我之前的Spring系列文章一樣,我們會以做一些Demo做實驗的方式來複習一些知識點。
本文我們先從Java併發中最最常用的執行緒池開始。

從一個執行緒池實驗開始

首先我們寫一個方法來每秒一次定時輸出執行緒池的基本資訊:

private void printStats(ThreadPoolExecutor threadPool){
    Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
        log.info("=========================");
        log.info("Pool Size: {}", threadPool.getPoolSize());
        log.info("Active Threads: {}", threadPool.getActiveCount());
        log.info("Number of Tasks Completed: {}", threadPool.getCompletedTaskCount());
        log.info("Number of Tasks in Queue: {}", threadPool.getQueue().size());

        log.info("=========================");
    }, 0, 1, TimeUnit.SECONDS);
}

然後,我們寫一個方法來定時提交任務到執行緒池:

private void submitTasks(AtomicInteger atomicInteger, ThreadPoolExecutor threadPool) {
    IntStream.rangeClosed(1, 20).forEach(i -> {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int id = atomicInteger.incrementAndGet();
        threadPool.submit(() -> {
            log.info("{} started", id);
            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("{} finished", id);
        });
    });
}

可以看到:

  • 一共提交20個任務
  • 每秒提交一個任務
  • 每一個任務執行耗時10秒
  • 任務內容很簡單,就是計數器累加

現在我們寫一個最普通的執行緒池,並且等待執行緒池所有任務執行完成:

@Test
public void test1() throws InterruptedException {
    AtomicInteger atomicInteger = new AtomicInteger();
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
            2, 5,
            5, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(10));

    printStats(threadPool);
    submitTasks(atomicInteger,threadPool);
    threadPool.shutdown();
    threadPool.awaitTermination(1, TimeUnit.HOURS);
}

可以看到,這個執行緒池是:

  • 2個核心執行緒
  • 5個最大執行緒
  • 5秒回收限制(最大)執行緒
  • 佇列最多10個任務

對於這樣的配置,大家想一下,任務提交過去後,執行緒池的工作方式是怎麼樣的?丟擲幾個問題:

  • 一開始執行緒池會有多少執行緒?
  • 慢慢的執行緒池的執行緒會變為多少?
  • 這些任務是否能執行完成?

我們執行後觀察一下輸出:

09:51:37.709 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:37.715 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 0
09:51:37.718 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 0
09:51:37.718 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
09:51:37.718 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 0
09:51:37.718 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:38.705 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:38.705 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 0
09:51:38.705 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 0
09:51:38.705 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
09:51:38.705 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 0
09:51:38.705 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:38.716 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 1 started
09:51:39.701 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:39.701 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 1
09:51:39.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 1
09:51:39.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
09:51:39.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 0
09:51:39.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:39.718 [pool-1-thread-2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 2 started
09:51:40.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:40.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 2
09:51:40.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 2
09:51:40.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
09:51:40.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 0
09:51:40.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:41.701 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:41.701 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 2
09:51:41.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 2
09:51:41.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
09:51:41.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 1
09:51:41.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:42.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:42.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 2
09:51:42.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 2
09:51:42.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
09:51:42.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 2
09:51:42.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:43.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:43.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 2
09:51:43.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 2
09:51:43.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
09:51:43.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 3
09:51:43.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:44.701 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:44.701 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 2
09:51:44.701 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 2
09:51:44.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
09:51:44.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 4
09:51:44.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:45.702 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:45.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 2
09:51:45.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 2
09:51:45.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
09:51:45.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 5
09:51:45.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:46.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:46.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 2
09:51:46.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 2
09:51:46.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
09:51:46.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 6
09:51:46.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:47.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:47.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 2
09:51:47.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 2
09:51:47.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
09:51:47.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 7
09:51:47.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:48.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:48.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 2
09:51:48.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 2
09:51:48.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
09:51:48.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 8
09:51:48.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:48.719 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 1 finished
09:51:48.719 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 3 started
09:51:49.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:49.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 2
09:51:49.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 2
09:51:49.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 1
09:51:49.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 8
09:51:49.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:49.720 [pool-1-thread-2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 2 finished
09:51:49.720 [pool-1-thread-2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 4 started
09:51:50.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:50.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 2
09:51:50.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 2
09:51:50.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 2
09:51:50.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 8
09:51:50.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:51.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:51.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 2
09:51:51.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 2
09:51:51.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 2
09:51:51.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 9
09:51:51.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:52.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:52.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 2
09:51:52.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 2
09:51:52.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 2
09:51:52.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 10
09:51:52.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:52.749 [pool-1-thread-3] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 15 started
09:51:53.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:53.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 3
09:51:53.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 3
09:51:53.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 2
09:51:53.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 10
09:51:53.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:53.754 [pool-1-thread-4] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 16 started
09:51:54.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:54.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 4
09:51:54.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 4
09:51:54.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 2
09:51:54.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 10
09:51:54.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:54.755 [pool-1-thread-5] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 17 started
09:51:55.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
09:51:55.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
09:51:55.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
09:51:55.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 2
09:51:55.703 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in queue: 10
09:51:55.704 [pool-2-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================

java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@fdefd3f[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@1a0dcaa[Wrapped task = me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest$$Lambda$50/0x0000000800105840@3bd40a57]] rejected from java.util.concurrent.ThreadPoolExecutor@d83da2e[Running, pool size = 5, active threads = 5, queued tasks = 10, completed tasks = 2]

    at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2055)
    at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:825)
    at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1355)
    at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:118)
    at me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest.lambda$submitTasks$2(ThreadPoolExecutorTest.java:33)
    at java.base/java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:104)
    at java.base/java.util.stream.IntPipeline$Head.forEach(IntPipeline.java:593)
    at me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest.submitTasks(ThreadPoolExecutorTest.java:26)
    at me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest.test1(ThreadPoolExecutorTest.java:54)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

通過輸出可以看到:

  • 雖然配置了2個核心執行緒,但是一開始執行緒池還是0個執行緒,你可以通過threadPool.prestartAllCoreThreads()來改變這個行為,初始化所有的核心執行緒
  • 隨著任務的提交,核心執行緒慢慢初始化,一直到2個核心執行緒
  • 因為任務執行需要10s,所以慢慢佇列裡面會堆積任務,任務慢慢堆積到10
  • 然後執行緒池會繼續新增執行緒,一直到最大執行緒數5
  • 到了最大執行緒數5的情況下,佇列也是打到了10滿了,這個時候還有任務進來就會出現拒絕執行的異常

我們知道,Java的執行緒池預設情況下是更傾向於使用佇列來存放任務而不是傾向於使用更多的執行緒來消化任務,只有當佇列滿了之後才會使用更多的執行緒池臨時處理一下突發的任務增多。
有的時候,我們其實更希望的是更激進的方式,執行緒池優先開啟更多的執行緒,而是把佇列當成一個後備方案來使用。比如我們這個例子,任務執行的很慢,需要10s,每秒提交一個任務,如果執行緒池可以彈性到5個最大執行緒,那麼20秒後有5個任務在執行,5個任務已經完成,還有10個線上程池裡,這些任務最終都可以完成,而不是像之前因為執行緒池過晚擴張導致慢任務來不及處理。

我們可以通過Hack的方式來實現:

@Test
public void test2() throws InterruptedException {
    AtomicInteger atomicInteger = new AtomicInteger();

    BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(10) {
        @Override
        public boolean offer(Runnable e) {
            return false;
        }
    };

    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
            2, 5,
            5, TimeUnit.SECONDS,
            queue, new ThreadFactoryImpl("elastic-pool"), (r, executor) -> {
        try {
            executor.getQueue().put(r);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    });

    threadPool.allowCoreThreadTimeOut(true);
    printStats(threadPool);
    submitTasks(atomicInteger,threadPool);
    threadPool.shutdown();
    threadPool.awaitTermination(1, TimeUnit.HOURS);
}

實現這個功能主要依靠兩個地方的改進:

  • 我們需要重寫佇列的offer方法,直接返回false,這樣就會讓執行緒池有佇列已經滿的錯覺,offer方法的定義如下:
    image_1dfkia9vgapk1k99pve1teelc99.png-133.3kB
  • 在初始化執行緒池的時候提供一個我們自定義的拒絕處理程式,這個時候(最大執行緒達到的情況)再去把任務真正插入到佇列中去,RejectedExecutionHandler定義如下:
    image_1dfkieh9k30l30o1hd210aseilm.png-186.3kB

我們來看一下輸出:

10:53:37.889 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:37.898 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 0
10:53:37.901 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 0
10:53:37.901 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
10:53:37.901 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 0
10:53:37.901 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:38.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:38.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 0
10:53:38.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 0
10:53:38.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
10:53:38.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 0
10:53:38.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:38.919 [elastic-pool1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 1 started
10:53:39.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:39.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 1
10:53:39.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 1
10:53:39.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
10:53:39.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 0
10:53:39.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:39.924 [elastic-pool2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 2 started
10:53:40.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:40.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 2
10:53:40.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 2
10:53:40.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
10:53:40.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 0
10:53:40.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:40.930 [elastic-pool3] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 3 started
10:53:41.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:41.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 3
10:53:41.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 3
10:53:41.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
10:53:41.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 0
10:53:41.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:41.934 [elastic-pool4] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 4 started
10:53:42.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:42.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 4
10:53:42.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 4
10:53:42.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
10:53:42.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 0
10:53:42.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:42.935 [elastic-pool5] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 5 started
10:53:43.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:43.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:53:43.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:53:43.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
10:53:43.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 0
10:53:43.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:44.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:44.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:53:44.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:53:44.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
10:53:44.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 1
10:53:44.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:45.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:45.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:53:45.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:53:45.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
10:53:45.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 2
10:53:45.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:46.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:46.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:53:46.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:53:46.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
10:53:46.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 3
10:53:46.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:47.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:47.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:53:47.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:53:47.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
10:53:47.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 4
10:53:47.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:48.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:48.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:53:48.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:53:48.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 0
10:53:48.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 5
10:53:48.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:48.921 [elastic-pool1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 1 finished
10:53:48.922 [elastic-pool1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 6 started
10:53:49.882 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:49.882 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:53:49.882 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:53:49.882 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 1
10:53:49.882 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 5
10:53:49.883 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:49.927 [elastic-pool2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 2 finished
10:53:49.928 [elastic-pool2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 7 started
10:53:50.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:50.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:53:50.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:53:50.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 2
10:53:50.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 5
10:53:50.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:50.933 [elastic-pool3] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 3 finished
10:53:50.933 [elastic-pool3] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 8 started
10:53:51.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:51.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:53:51.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:53:51.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 3
10:53:51.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 5
10:53:51.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:51.935 [elastic-pool4] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 4 finished
10:53:51.935 [elastic-pool4] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 9 started
10:53:52.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:52.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:53:52.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:53:52.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 4
10:53:52.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 5
10:53:52.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:52.937 [elastic-pool5] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 5 finished
10:53:52.938 [elastic-pool5] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 10 started
10:53:53.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:53.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:53:53.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:53:53.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 5
10:53:53.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 5
10:53:53.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:54.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:54.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:53:54.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:53:54.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 5
10:53:54.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 6
10:53:54.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:55.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:55.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:53:55.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:53:55.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 5
10:53:55.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 7
10:53:55.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:56.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:56.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:53:56.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:53:56.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 5
10:53:56.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 8
10:53:56.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:57.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:57.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:53:57.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:53:57.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 5
10:53:57.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 9
10:53:57.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:58.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:58.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:53:58.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:53:58.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 5
10:53:58.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 10
10:53:58.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:58.923 [elastic-pool1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 6 finished
10:53:58.923 [elastic-pool1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 11 started
10:53:59.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:59.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:53:59.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:53:59.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 6
10:53:59.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 9
10:53:59.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:53:59.931 [elastic-pool2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 7 finished
10:53:59.931 [elastic-pool2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 12 started
10:54:00.883 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:00.883 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:00.883 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:00.883 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 7
10:54:00.883 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 8
10:54:00.883 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:00.937 [elastic-pool3] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 8 finished
10:54:00.938 [elastic-pool3] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 13 started
10:54:01.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:01.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:01.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:01.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 8
10:54:01.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 7
10:54:01.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:01.937 [elastic-pool4] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 9 finished
10:54:01.937 [elastic-pool4] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 14 started
10:54:02.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:02.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:02.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:02.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 9
10:54:02.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 6
10:54:02.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:02.941 [elastic-pool5] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 10 finished
10:54:02.941 [elastic-pool5] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 15 started
10:54:03.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:03.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:03.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:03.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 10
10:54:03.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 5
10:54:03.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:04.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:04.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:04.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:04.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 10
10:54:04.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 5
10:54:04.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:05.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:05.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:05.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:05.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 10
10:54:05.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 5
10:54:05.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:06.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:06.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:06.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:06.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 10
10:54:06.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 5
10:54:06.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:07.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:07.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:07.882 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:07.882 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 10
10:54:07.882 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 5
10:54:07.882 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:08.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:08.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:08.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:08.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 10
10:54:08.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 5
10:54:08.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:08.927 [elastic-pool1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 11 finished
10:54:08.928 [elastic-pool1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 16 started
10:54:09.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:09.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:09.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:09.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 11
10:54:09.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 4
10:54:09.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:09.933 [elastic-pool2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 12 finished
10:54:09.933 [elastic-pool2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 17 started
10:54:10.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:10.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:10.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:10.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 12
10:54:10.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 3
10:54:10.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:10.942 [elastic-pool3] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 13 finished
10:54:10.942 [elastic-pool3] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 18 started
10:54:11.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:11.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:11.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:11.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 13
10:54:11.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 2
10:54:11.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:11.939 [elastic-pool4] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 14 finished
10:54:11.939 [elastic-pool4] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 19 started
10:54:12.881 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:12.882 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:12.882 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:12.882 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 14
10:54:12.882 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 1
10:54:12.882 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:12.946 [elastic-pool5] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 15 finished
10:54:12.946 [elastic-pool5] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 20 started
10:54:13.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:13.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:13.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:13.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 15
10:54:13.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 0
10:54:13.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:14.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:14.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:14.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:14.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 15
10:54:14.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 0
10:54:14.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:15.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:15.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:15.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:15.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 15
10:54:15.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 0
10:54:15.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:16.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:16.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:16.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:16.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 15
10:54:16.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 0
10:54:16.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:17.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:17.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:17.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:17.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 15
10:54:17.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 0
10:54:17.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:18.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:18.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 5
10:54:18.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 5
10:54:18.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 15
10:54:18.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 0
10:54:18.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:18.929 [elastic-pool1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 16 finished
10:54:19.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:19.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 4
10:54:19.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 4
10:54:19.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 16
10:54:19.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 0
10:54:19.880 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:19.938 [elastic-pool2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 17 finished
10:54:20.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:20.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 3
10:54:20.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 3
10:54:20.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 17
10:54:20.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 0
10:54:20.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:20.947 [elastic-pool3] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 18 finished
10:54:21.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:21.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 2
10:54:21.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 2
10:54:21.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 18
10:54:21.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 0
10:54:21.879 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:21.943 [elastic-pool4] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 19 finished
10:54:22.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:22.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Pool Size: 1
10:54:22.877 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Active Threads: 1
10:54:22.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks Completed: 19
10:54:22.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - Number of Tasks in Queue: 0
10:54:22.878 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - =========================
10:54:22.950 [elastic-pool5] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.ThreadPoolExecutorTest - 20 finished

從這個結果可以看到:

  • 一開始執行緒池因為沒有預先初始化,所以還是0個執行緒
  • 然後直接拉到了最大執行緒5個執行緒
  • 所有任務都可以執行完成
  • 最後因為我們開啟了allowCoreThreadTimeOut,所以核心執行緒也可以得到回收

其實,這裡還可以想到另一種思路,既然我們希望在高併發的情況下,執行緒池能夠更積極建立執行緒,那麼我們還可以初始化執行緒池的時候就初始化5個核心執行緒,5個最大執行緒,然後開啟核心執行緒回收,這樣也可以在併發低的時候節約執行緒。

當然這裡的這個實現非常簡單粗暴,其實什麼時候佇列返回false是一種學問,我們最好有一定的緩衝,不行的話再建立新的執行緒,也不一定就像這裡的直接返回false,後面我們會提到tomcat執行緒池更好的實現。

為執行緒池的執行緒命名

在之前的測試中我們使用到了一個自定義的執行緒工廠類來為執行緒命名:

public class ThreadFactoryImpl implements ThreadFactory {
    private final AtomicLong threadIndex = new AtomicLong(0);
    private final String threadNamePrefix;
    private final boolean daemon;

    public ThreadFactoryImpl(final String threadNamePrefix) {
        this(threadNamePrefix, false);
    }

    public ThreadFactoryImpl(final String threadNamePrefix, boolean daemon) {
        this.threadNamePrefix = threadNamePrefix;
        this.daemon = daemon;
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread thread = new Thread(r, threadNamePrefix + this.threadIndex.incrementAndGet());
        thread.setDaemon(daemon);
        return thread;
    }
}

這是一個非常好的實踐,對於複雜的專案一定有N多個執行緒池,有了明確的容易識別的命名,更容易在出現問題的時候進行排查。

Tomcat執行緒池

Tomcat執行緒池基於原生執行緒池做了一些改進,主要在三方面:

  • 一開始就初始化核心執行緒,之後這些執行緒肯定會用滿,所以何不一開始就初始化呢
  • 和之前的一樣,會盡可能用足執行緒而不是去先使用佇列(tomcat的佇列是無限長的佇列,所以這樣能確保能使用到最大的執行緒,否則最大執行緒這個配置就沒意義)
  • 在出現佇列滿的時候發生任務提交失敗異常的時候再做一下最後的嘗試,把任務提交到佇列中去,而且允許有一定的等待時間

下面看下相關程式碼:
首先是佇列這塊,我們重點關注下重寫的offer方法,返回false來增加執行緒的時機:

@Slf4j
public class TomcatTaskQueue extends LinkedBlockingQueue<Runnable> {

private transient volatile TomcatThreadPool parent = null;

public TomcatTaskQueue(int capacity) {
    super(capacity);
}

public void setParent(TomcatThreadPool tp) {
    parent = tp;
}

public boolean force(Runnable o) {
    if (parent == null || parent.isShutdown())
        throw new RejectedExecutionException("taskQueue.notRunning");
    return super.offer(o); //forces the item onto the queue, to be used if the task is rejected
}

public boolean force(Runnable o, long timeout, TimeUnit unit) throws InterruptedException {
    if (parent == null || parent.isShutdown())
        throw new RejectedExecutionException("taskQueue.notRunning");
    return super.offer(o, timeout, unit); //forces the item onto the queue, to be used if the task is rejected
}

@Override
public boolean offer(Runnable o) {
    //we can't do any checks
    if (parent == null)
        return super.offer(o);
    //we are maxed out on threads, simply queue the object
    if (parent.getPoolSize() == parent.getMaximumPoolSize()) {
        log.info("pool==max, getPoolSize: {}, getMaximumPoolSize:{}, task:{}", parent.getPoolSize(), parent.getMaximumPoolSize(), o);
        return super.offer(o);
    }
    //we have idle threads, just add it to the queue
    if (parent.getSubmittedCount() <= (parent.getPoolSize())) {
        log.info("submit<=pool, getPoolSize: {}, getMaximumPoolSize:{}, task:{}", parent.getPoolSize(), parent.getMaximumPoolSize(), o);
        return super.offer(o);
    }
    //if we have less threads than maximum force creation of a new thread
    if (parent.getPoolSize() < parent.getMaximumPoolSize()) {
        log.info("Grow thread pool, getPoolSize: {}, getMaximumPoolSize:{}", parent.getPoolSize(), parent.getMaximumPoolSize());
        return false;
    }
    //if we reached here, we need to add it to the queue
    log.info("else, getPoolSize: {}, getMaximumPoolSize:{}, task:{}", parent.getPoolSize(), parent.getMaximumPoolSize(), o);
    return super.offer(o);
}

然後是執行緒池這塊,我們重點注意出現拒絕異常後的處理策略:

@Slf4j
public class TomcatThreadPool extends java.util.concurrent.ThreadPoolExecutor {

    /**
     * The number of tasks submitted but not yet finished. This includes tasks
     * in the queue and tasks that have been handed to a worker thread but the
     * latter did not start executing the task yet.
     * This number is always greater or equal to {@link #getActiveCount()}.
     */
    private final AtomicInteger submittedCount = new AtomicInteger(0);

    public TomcatThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
        prestartAllCoreThreads();
    }

    public TomcatThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
                            RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
        prestartAllCoreThreads();
    }

    public TomcatThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, new RejectHandler());
        prestartAllCoreThreads();
    }

    public TomcatThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new RejectHandler());
        prestartAllCoreThreads();
    }

    public int getSubmittedCount() {
        return submittedCount.get();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void execute(Runnable command) {
        execute(command, 0, TimeUnit.MILLISECONDS);
    }

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the <tt>Executor</tt> implementation.
     * If no threads are available, it will be added to the work queue.
     * If the work queue is full, the system will wait for the specified
     * time and it throw a RejectedExecutionException if the queue is still
     * full after that.
     *
     * @param command the runnable task
     * @param timeout A timeout for the completion of the task
     * @param unit    The timeout time unit
     * @throws RejectedExecutionException if this task cannot be
     *                                    accepted for execution - the queue is full
     * @throws NullPointerException       if command or unit is null
     */
    public void execute(Runnable command, long timeout, TimeUnit unit) {
        submittedCount.incrementAndGet();
        try {
            super.execute(command);
        } catch (RejectedExecutionException rx) {
            if (super.getQueue() instanceof TomcatTaskQueue) {
                final TomcatTaskQueue queue = (TomcatTaskQueue) super.getQueue();
                try {
                    if (!queue.force(command, timeout, unit)) {
                        submittedCount.decrementAndGet();
                        throw new RejectedExecutionException("threadPoolExecutor.queueFull");
                    } else {
                        log.warn("RejectedExecutionException throw, task {} put into queue again", command.toString());
                    }
                } catch (InterruptedException x) {
                    submittedCount.decrementAndGet();
                    throw new RejectedExecutionException(x);
                }
            } else {
                submittedCount.decrementAndGet();
                throw rx;
            }

        }
    }

    private static class RejectHandler implements RejectedExecutionHandler {
        @Override
        public void rejectedExecution(Runnable r,
                                      java.util.concurrent.ThreadPoolExecutor executor) {
            throw new RejectedExecutionException();
        }

    }
}

現在來寫一段測試程式碼:

@Slf4j
public class TomcatThreadPoolTest {

    @Test
    public void test() throws InterruptedException {
        TomcatTaskQueue taskqueue = new TomcatTaskQueue(5);
        TomcatThreadPool threadPool = new TomcatThreadPool(2, 5, 60, TimeUnit.SECONDS, taskqueue);
        taskqueue.setParent(threadPool);
        IntStream.rangeClosed(1, 10).forEach(i -> threadPool.execute(new Task(true, i)));
        IntStream.rangeClosed(1, 10).forEach(i -> threadPool.execute(new Task(false, i)
                , 1050, TimeUnit.MILLISECONDS));

        threadPool.shutdown();
        threadPool.awaitTermination(1, TimeUnit.HOURS);
    }

    @ToString
    class Task implements Runnable {
        private boolean slow;
        private String name;

        public Task(boolean slow, int index) {
            this.slow = slow;
            this.name = String.format("%s-%d", slow ? "slow" : "quick", index);
        }

        @Override
        public void run() {
            log.info("Start:{}", name);
            if (slow) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.info("Finish:{}", name);
        }
    }
}

在測試程式碼中,我們的佇列只有5,我們的最大執行緒也是5,而我們的慢任務是10個,因為我們的任務允許等待1050ms嘗試提交到佇列,所以基於這樣的配置,我們的快任務也能順利執行完成,而不會出錯:

16:38:36.117 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - submit<=pool, getPoolSize: 2, getMaximumPoolSize:5, task:TomcatThreadPoolTest.Task(slow=true, name=slow-1)
16:38:36.165 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:slow-1
16:38:36.166 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - submit<=pool, getPoolSize: 2, getMaximumPoolSize:5, task:TomcatThreadPoolTest.Task(slow=true, name=slow-2)
16:38:36.166 [pool-1-thread-2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:slow-2
16:38:36.166 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - Grow thread pool, getPoolSize: 2, getMaximumPoolSize:5
16:38:36.166 [pool-1-thread-3] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:slow-3
16:38:36.166 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - Grow thread pool, getPoolSize: 3, getMaximumPoolSize:5
16:38:36.167 [pool-1-thread-4] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:slow-4
16:38:36.167 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - Grow thread pool, getPoolSize: 4, getMaximumPoolSize:5
16:38:36.167 [pool-1-thread-5] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:slow-5
16:38:36.168 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - pool==max, getPoolSize: 5, getMaximumPoolSize:5, task:TomcatThreadPoolTest.Task(slow=true, name=slow-6)
16:38:36.168 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - pool==max, getPoolSize: 5, getMaximumPoolSize:5, task:TomcatThreadPoolTest.Task(slow=true, name=slow-7)
16:38:36.168 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - pool==max, getPoolSize: 5, getMaximumPoolSize:5, task:TomcatThreadPoolTest.Task(slow=true, name=slow-8)
16:38:36.169 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - pool==max, getPoolSize: 5, getMaximumPoolSize:5, task:TomcatThreadPoolTest.Task(slow=true, name=slow-9)
16:38:36.169 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - pool==max, getPoolSize: 5, getMaximumPoolSize:5, task:TomcatThreadPoolTest.Task(slow=true, name=slow-10)
16:38:36.170 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - pool==max, getPoolSize: 5, getMaximumPoolSize:5, task:TomcatThreadPoolTest.Task(slow=false, name=quick-1)
16:38:37.169 [pool-1-thread-2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:slow-2
16:38:37.169 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:slow-1
16:38:37.169 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:slow-7
16:38:37.169 [pool-1-thread-2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:slow-6
16:38:37.169 [main] WARN me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPool - RejectedExecutionException throw, task TomcatThreadPoolTest.Task(slow=false, name=quick-1) put into queue again
16:38:37.169 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - pool==max, getPoolSize: 5, getMaximumPoolSize:5, task:TomcatThreadPoolTest.Task(slow=false, name=quick-2)
16:38:37.170 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - pool==max, getPoolSize: 5, getMaximumPoolSize:5, task:TomcatThreadPoolTest.Task(slow=false, name=quick-3)
16:38:37.170 [pool-1-thread-4] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:slow-4
16:38:37.170 [pool-1-thread-3] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:slow-3
16:38:37.170 [pool-1-thread-5] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:slow-5
16:38:37.170 [pool-1-thread-4] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:slow-8
16:38:37.170 [main] WARN me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPool - RejectedExecutionException throw, task TomcatThreadPoolTest.Task(slow=false, name=quick-3) put into queue again
16:38:37.170 [pool-1-thread-5] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:slow-10
16:38:37.170 [pool-1-thread-3] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:slow-9
16:38:37.171 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - pool==max, getPoolSize: 5, getMaximumPoolSize:5, task:TomcatThreadPoolTest.Task(slow=false, name=quick-4)
16:38:37.171 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - pool==max, getPoolSize: 5, getMaximumPoolSize:5, task:TomcatThreadPoolTest.Task(slow=false, name=quick-5)
16:38:37.171 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - pool==max, getPoolSize: 5, getMaximumPoolSize:5, task:TomcatThreadPoolTest.Task(slow=false, name=quick-6)
16:38:38.171 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:slow-7
16:38:38.171 [pool-1-thread-2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:slow-6
16:38:38.172 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:quick-1
16:38:38.172 [main] WARN me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPool - RejectedExecutionException throw, task TomcatThreadPoolTest.Task(slow=false, name=quick-6) put into queue again
16:38:38.172 [pool-1-thread-2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:quick-2
16:38:38.172 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:quick-1
16:38:38.172 [pool-1-thread-2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:quick-2
16:38:38.172 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:quick-3
16:38:38.172 [pool-1-thread-2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:quick-4
16:38:38.172 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:quick-3
16:38:38.172 [pool-1-thread-2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:quick-4
16:38:38.172 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:quick-5
16:38:38.172 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - pool==max, getPoolSize: 5, getMaximumPoolSize:5, task:TomcatThreadPoolTest.Task(slow=false, name=quick-7)
16:38:38.172 [pool-1-thread-2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:quick-6
16:38:38.172 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:quick-5
16:38:38.172 [pool-1-thread-2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:quick-6
16:38:38.172 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:quick-7
16:38:38.172 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:quick-7
16:38:38.172 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - pool==max, getPoolSize: 5, getMaximumPoolSize:5, task:TomcatThreadPoolTest.Task(slow=false, name=quick-8)
16:38:38.173 [pool-1-thread-2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:quick-8
16:38:38.173 [pool-1-thread-2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:quick-8
16:38:38.173 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - pool==max, getPoolSize: 5, getMaximumPoolSize:5, task:TomcatThreadPoolTest.Task(slow=false, name=quick-9)
16:38:38.173 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:quick-9
16:38:38.173 [pool-1-thread-1] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:quick-9
16:38:38.173 [main] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatTaskQueue - pool==max, getPoolSize: 5, getMaximumPoolSize:5, task:TomcatThreadPoolTest.Task(slow=false, name=quick-10)
16:38:38.173 [pool-1-thread-2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Start:quick-10
16:38:38.174 [pool-1-thread-2] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:quick-10
16:38:38.175 [pool-1-thread-4] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:slow-8
16:38:38.175 [pool-1-thread-3] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:slow-9
16:38:38.175 [pool-1-thread-5] INFO me.josephzhu.javaconcurrenttest.concurrent.executors.TomcatThreadPoolTest - Finish:slow-10

注意日誌中出現了好幾次RejectedExecutionException,但是經過等待任務都提交到了佇列中。

預定義的執行緒池

我們看看Executors預定義的兩種所謂常用的執行緒池:

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

所謂固定大小的執行緒池就是執行緒大小固定,但是佇列是無界的。顯然,如果任務非常多非常慢超過了執行緒池的處理能力,產生了堆積,那麼這個無界佇列可能會導致OOM這是一個風險:
image_1dflmvolmfc7122k17841ut61tnj79.png-359.3kB

還有一種所謂快取執行緒池,這個快取意思是執行緒是快取著用的,60秒回收,需要的話就建立,執行緒數量無上限,需要的話會一直建立。這個也很極端,可能會建立過多執行緒,同樣會導致OOM:
image_1dflftfnmr651qbnfrsrs21je06s.png-364.1kB

測試程式碼就不給出了,你可以自己做實驗。
所以阿里Java開發手冊不建議使用這兩種預定義的執行緒池,我們應該自己根據需要控制執行緒池的:

  • 佇列型別
  • 佇列長度
  • 核心執行緒數
  • 最大執行緒數
  • 回收時間
  • 是否回收核心執行緒
  • 是否預建立核心執行緒
  • 拒絕處理方式
  • 執行緒工廠

可暫停的執行緒池

這裡我們給出官方的例子,通過實現自定義的beforeExecute來實現可暫停的執行緒池。
image_1dfl8ntbk155rpm71f3c1o8r15et13.png-143kB

執行緒池程式碼如下:

public class PausableThreadPoolExecutor extends ThreadPoolExecutor {
    private boolean isPaused;
    private ReentrantLock pauseLock = new ReentrantLock();
    private Condition unpaused = pauseLock.newCondition();

    public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
    }

    public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
    }

    public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }
    
    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        pauseLock.lock();
        try {
            while (isPaused) unpaused.await();
        } catch (InterruptedException ie) {
            t.interrupt();
        } finally {
            pauseLock.unlock();
        }
    }

    public void pause() {
        pauseLock.lock();
        try {
            isPaused = true;
        } finally {
            pauseLock.unlock();
        }
    }

    public void resume() {
        pauseLock.lock();
        try {
            isPaused = false;
            unpaused.signalAll();
        } finally {
            pauseLock.unlock();
        }
    }
}

寫一段測試程式碼試試:

@Test
public void test() throws InterruptedException {
    PausableThreadPoolExecutor threadPool = new PausableThreadPoolExecutor(1,1,0,TimeUnit.HOURS,new LinkedBlockingQueue<>());
    IntStream.rangeClosed(1, 5).forEach(i->threadPool.submit(()->{
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("I'm done : {}", i);
    }));
    ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    scheduler.schedule(threadPool::pause, 2, TimeUnit.SECONDS);
    scheduler.schedule(threadPool::resume, 4, TimeUnit.SECONDS);

    threadPool.shutdown();
    threadPool.awaitTermination(1, TimeUnit.HOURS);
}

在測試程式碼中,我們的執行緒池只有一個執行緒,我們提交五個任務進去,每一個任務需要執行1秒,我們使用另一個定時任務執行緒池來定時開關這個可暫停的執行緒池。執行結果如下:
image_1dfl9f4lt1e45a581ob11bqa4hr37.png-197.3kB
可以看到,這個執行緒池在2秒後暫停了4秒後恢復了。

定時任務執行緒池

在剛才的測試中,我們用到了定時任務執行緒池,這裡我們再做一個測試。
我們來測試一下scheduleAtFixedRate和scheduleWithFixedDelay的區別。
在下面的程式碼裡,我們分別進行兩次測試:

  • 一次是讓任務在固定的頻率去執行,一次是讓任務具有固定的延遲去執行
  • 然後1秒後關閉定時任務執行緒池(也是通過這個定時任務的排程功能去實現)
  • 主執行緒一直等待到執行緒池終止
@Test
public void test1() throws InterruptedException {
    AtomicInteger scheduleAtFixedRateTotal = new AtomicInteger();
    ScheduledExecutorService scheduleAtFixedRateExecutorService = Executors.newSingleThreadScheduledExecutor();
    ScheduledFuture scheduleAtFixedRateTotalFuture = scheduleAtFixedRateExecutorService.scheduleAtFixedRate(() -> {
        try {
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("scheduleAtFixedRate:" + scheduleAtFixedRateTotal.incrementAndGet());
    }, 0, 100, TimeUnit.MILLISECONDS);
    scheduleAtFixedRateExecutorService.schedule(() -> scheduleAtFixedRateTotalFuture.cancel(false), 1, TimeUnit.SECONDS);
    while (!scheduleAtFixedRateTotalFuture.isDone()) TimeUnit.MILLISECONDS.sleep(1);
    Assert.assertEquals(11, scheduleAtFixedRateTotal.get());

    AtomicInteger scheduleWithFixedDelayTotal = new AtomicInteger();
    ScheduledExecutorService scheduleWithFixedDelayExecutorService = Executors.newSingleThreadScheduledExecutor();
    ScheduledFuture scheduleWithFixedDelayFuture = scheduleWithFixedDelayExecutorService.scheduleWithFixedDelay(() -> {
        try {
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("scheduleWithFixedDelay:" + scheduleWithFixedDelayTotal.incrementAndGet());
    }, 0, 100, TimeUnit.MILLISECONDS);
    scheduleWithFixedDelayExecutorService.schedule(() -> scheduleWithFixedDelayFuture.cancel(false), 1, TimeUnit.SECONDS);
    while (!scheduleWithFixedDelayFuture.isDone()) TimeUnit.MILLISECONDS.sleep(1);
    Assert.assertEquals(5, scheduleWithFixedDelayTotal.get());
}

通過斷言我們也可以知道,固定頻率執行任務"忽略"了任務執行的時間,所以能執行更多次數,最終在1秒執行了11次。固定延遲執行任務是在任務完成後再延遲固定的間隔去執行,最終只能在1秒左右的時間執行5次。
下面是輸出:
image_1dfl9s69u19tgh3s10r911j21t5g3k.png-520.1kB

在這裡,忽略兩個字,我打上了雙引號,那是因為對於單執行緒的定時任務執行緒池,任務如果執行太慢,比頻率還慢,那麼執行緒池也沒這個能力去fixRate執行。這裡你可以嘗試一下把休眠時間100ms修改為200ms,重新執行後結果如下:
image_1dfladh1p1k721fl31dc1tuv1t1e41.png-367.4kB
什麼?單元測試還是通過的?難道不是應該執行了5次就結束了嗎?注意觀察,我們的執行緒池並沒有在1秒後結束,而是維持了2秒多,還是老問題,因為是單執行緒,我們的關閉執行緒池的那個任務也無法及時得到執行。
嘗試修改程式碼,把cancel任務放到獨立的執行緒池去執行:

Executors.newSingleThreadScheduledExecutor().schedule(() -> scheduleAtFixedRateTotalFuture.cancel(false), 1, TimeUnit.SECONDS);

(或者也可以使用2個執行緒的定時任務執行緒池)
可以看到,這次的結果符合預期:
image_1dflam0bu12d21qik532581163n4e.png-162.3kB

ForkJoin執行緒池

從Java 1.8開始,Executors提供了newWorkStealingPool來獲得一個ForkJoin執行緒池。
從命名可以看到,這是一個工作竊取執行緒池,傳統的執行緒池具有公共的一個任務佇列,在任務很多,任務執行很快(CPU密集型任務)的情況下,會發生比較多的競爭問題,而ForkJoin的話每一個執行緒都有自己的任務佇列,如果自己的佇列沒有任務的話可以從其它佇列竊取任務,這樣確保了吞吐的同時,減少了競爭。我們寫一段程式碼來比較一下:

image_1dflb1rdqa2q34inem1mb8end4r.png-194.3kB

@Slf4j
public class ForkJoinPoolBenchmark {
    @Test
    public void test() throws InterruptedException {
        AtomicLong atomicLong = new AtomicLong();
        StopWatch stopWatch = new StopWatch();
        ExecutorService normal = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
        ExecutorService forkjoin = Executors.newWorkStealingPool(Runtime.getRuntime().availableProcessors());

        stopWatch.start("normal");
        LongStream.rangeClosed(1, 10000000).forEach(__->normal.submit(atomicLong::incrementAndGet));
        normal.shutdown();
        normal.awaitTermination(1, TimeUnit.HOURS);
        stopWatch.stop();
        long r = atomicLong.get();
        stopWatch.start("forkjoin");
        LongStream.rangeClosed(1, 10000000).forEach(__->forkjoin.submit(atomicLong::incrementAndGet));
        forkjoin.shutdown();
        forkjoin.awaitTermination(1, TimeUnit.HOURS);
        stopWatch.stop();
        log.info(stopWatch.prettyPrint());
        log.info("result:{},{}", r, atomicLong.get());
    }
}

在這裡,我們的任務很簡單就是不斷++一個AtomicLong。測試結果如下:
image_1dflbna1ncr2484ter12m7sir58.png-116.8kB
可以看到,很明顯forkjoin更快。

執行緒池的任務出了異常會怎麼樣?

經常發現有一些小夥伴寫程式碼,在把任務提交到執行緒池後,任務經常會出異常,出異常也不管,他們說沒事,執行緒池會捕獲異常的。真的是這樣嗎?寫段程式碼試試:

@Slf4j
public class ThreadPoolExceptionTest {

    @Before
    public void setDefaultUncaughtExceptionHandler(){
        Thread.setDefaultUncaughtExceptionHandler((Thread t, Throwable e)->{
            log.warn("Exception in thread {}", t,e);
        });
    }

    @Test
    public void test() throws InterruptedException {
        String prefix = "test";
        ExecutorService threadPool = Executors.newFixedThreadPool(1, new ThreadFactoryImpl(prefix));
        IntStream.rangeClosed(1, 10).forEach(i -> threadPool.execute(() -> {
            if (i == 5) throw new RuntimeException("error");
            log.info("I'm done : {}", i);
            if (i < 5) Assert.assertEquals(prefix + "1", Thread.currentThread().getName());
            else Assert.assertEquals(prefix + "2", Thread.currentThread().getName());
        }));

        threadPool.shutdown();
        threadPool.awaitTermination(1, TimeUnit.HOURS);
    }
}

在這裡我們一共有10個任務,執行到第五個任務的時候,會主動丟擲一個異常。
從斷言可以看到,一開始的任務是在test1執行緒上執行的,因為出了異常後面的任務就在test2執行緒上執行了,看看執行結果:
image_1dflca9f9rdi4iiqbaj4m2bk5l.png-389.5kB
結果上也可以看到幾個結論:

  • 執行緒池裡的任務出了未處理異常,最終這個異常算是未處理異常,可以由未處理異常進行處理
  • 這個執行緒會終止,而不是傳說的執行緒池會捕獲異常
  • 執行緒池只能重新建立新的異常來填補空白,這是有代價的

不過這個結論不完全正確,我們嘗試把execute改為submit來試試:

@Test
public void test() throws InterruptedException {
    String prefix = "test";
    ExecutorService threadPool = Executors.newFixedThreadPool(1, new ThreadFactoryImpl(prefix));
    List<Future> futures = new ArrayList<>();
    IntStream.rangeClosed(1, 10).forEach(i -> futures.add(threadPool.submit(() -> {
        if (i == 5) throw new RuntimeException("error");
        log.info("I'm done : {}", i);
//            if (i < 5) Assert.assertEquals(prefix + "1", Thread.currentThread().getName());
//            else Assert.assertEquals(prefix + "2", Thread.currentThread().getName());
    })));

    for (Future future : futures) {
        try {
            future.get();
        } catch (ExecutionException e) {
            log.warn("future ExecutionException",e);
        }
    }
    threadPool.shutdown();
    threadPool.awaitTermination(1, TimeUnit.HOURS);
}

輸出結果如下:
image_1dfld304t1jp4c1og8d13mp3sh62.png-484kB
可以看到這次不一樣了,異常會在Future.get()的時候拿到,因此執行緒也不會死亡。
我們再寫一段程式碼比較下效能(需要註釋setDefaultUncaughtExceptionHandler中的log.warn避免輸出異常):

@Test
public void test2() throws InterruptedException {
    StopWatch stopWatch = new StopWatch();
    ExecutorService threadPool1 = Executors.newFixedThreadPool(1);
    stopWatch.start("execute");
    IntStream.rangeClosed(1, 100000).forEach(i->threadPool1.execute(()->{
        throw new RuntimeException("error");
    }));
    threadPool1.shutdown();
    threadPool1.awaitTermination(1, TimeUnit.HOURS);
    stopWatch.stop();
    ExecutorService threadPool2 = Executors.newFixedThreadPool(1);
    stopWatch.start("submit");
    IntStream.rangeClosed(1, 100000).forEach(i->threadPool2.submit(()->{
        throw new RuntimeException("error");
    }));
    threadPool2.shutdown();
    threadPool2.awaitTermination(1, TimeUnit.HOURS);
    stopWatch.stop();
    log.info(stopWatch.prettyPrint());
}

結果如下:
image_1dfldk31i7h616dgu2odk6clg6f.png-78.1kB
這就更證實了之前說的重新建立執行緒的代價問題。你還可以自己測試一下,定時任務執行緒池的任務出異常了會怎麼樣?

程式碼

本文的所有程式碼見我的Github Repo:https://github.com/JosephZhu1983/java-concurrent-test
程式碼的測試基於JDK11

總結

本文我們做了十來個實驗,各種測試執行緒池。總結下來幾點:

  • 執行緒池最好手動建立,不要用系統所謂的預定義的常用執行緒池
  • 在極端情況下可能需要修改預設執行緒池建立執行緒的行為
  • 注意執行緒池中的任務建議還是不要出現未處理異常為好
  • 使用定時任務執行緒池進行任務執行注意執行緒池大小問題

相關文章