和朱晔一起复习Java并发(一):线程池

和我之前的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 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方法的定义如下:
    和朱晔一起复习Java并发(一):线程池
  • 在初始化线程池的时候提供一个我们自定义的拒绝处理程序,这个时候(最大线程达到的情况)再去把任务真正插入到队列中去,RejectedExecutionHandler定义如下:
    和朱晔一起复习Java并发(一):线程池

我们来看一下输出:

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 {

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

然后是线程池这块,我们重点注意出现拒绝异常后的处理策略:

@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 workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
        prestartAllCoreThreads();
    }

    public TomcatThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue 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 workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, new RejectHandler());
        prestartAllCoreThreads();
    }

    public TomcatThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue 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 Executor 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] me.josephzhu.javaconcurrenttest.concurrent.executors.tomcattaskqueue submit<="pool," [pool-1-thread-2] start:slow-2 grow thread pool, getmaximumpoolsize:5 [pool-1-thread-3] start:slow-3 3, 16:38:36.167 [pool-1-thread-4] start:slow-4 4, [pool-1-thread-5] start:slow-5 16:38:36.168 pool="=max," 5, 16:38:36.169 16:38:36.170 16:38:37.169 finish:slow-2 finish:slow-1 start:slow-7 start:slow-6 warn me.josephzhu.javaconcurrenttest.concurrent.executors.tomcatthreadpool rejectedexecutionexception throw, task tomcatthreadpooltest.task(slow="false," put into queue again 16:38:37.170 finish:slow-4 finish:slow-3 finish:slow-5 start:slow-8 start:slow-10 start:slow-9 16:38:37.171 16:38:38.171 finish:slow-7 finish:slow-6 16:38:38.172 start:quick-1 start:quick-2 finish:quick-1 finish:quick-2 start:quick-3 start:quick-4 finish:quick-3 finish:quick-4 start:quick-5 start:quick-6 finish:quick-5 finish:quick-6 start:quick-7 finish:quick-7 16:38:38.173 start:quick-8 finish:quick-8 start:quick-9 finish:quick-9 start:quick-10 16:38:38.174 finish:quick-10 16:38:38.175 finish:slow-8 finish:slow-9 finish:slow-10 < code></=pool,>

注意日志中出现了好几次RejectedExecutionException,但是经过等待任务都提交到了队列中。

预定义的线程池

我们看看Executors预定义的两种所谓 常用的线程池:

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

所谓固定大小的线程池就是线程大小固定,但是队列是无界的。显然,如果任务非常多非常慢超过了线程池的处理能力,产生了堆积,那么这个无界队列可能会导致OOM这是一个风险:

和朱晔一起复习Java并发(一):线程池

还有一种所谓缓存线程池,这个缓存意思是线程是缓存着用的,60秒回收,需要的话就创建,线程数量无上限,需要的话会一直创建。这个也很极端,可能会创建过多线程,同样会导致OOM:

和朱晔一起复习Java并发(一):线程池

测试代码就不给出了,你可以自己做实验。
所以阿里Java开发手册不建议使用这两种预定义的线程池,我们应该自己根据需要控制线程池的:

  • 队列类型
  • 队列长度
  • 核心线程数
  • 最大线程数
  • 回收时间
  • 是否回收核心线程
  • 是否预创建核心线程
  • 拒绝处理方式
  • 线程工厂

可暂停的线程池

这里我们给出官方的例子,通过实现自定义的beforeExecute来实现可暂停的线程池。

和朱晔一起复习Java并发(一):线程池

线程池代码如下:

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 workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

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

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

    public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue 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秒,我们使用另一个定时任务线程池来定时开关这个可暂停的线程池。运行结果如下:

和朱晔一起复习Java并发(一):线程池
可以看到,这个线程池在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次。
下面是输出:

和朱晔一起复习Java并发(一):线程池

在这里,忽略两个字,我打上了双引号,那是因为对于单线程的定时任务线程池,任务如果执行太慢,比频率还慢,那么线程池也没这个能力去fixRate执行。这里你可以尝试一下把休眠时间100ms修改为200ms,重新运行后结果如下:

和朱晔一起复习Java并发(一):线程池
什么?单元测试还是通过的?难道不是应该运行了5次就结束了吗?注意观察,我们的线程池并没有在1秒后结束,而是维持了2秒多,还是老问题,因为是单线程,我们的关闭线程池的那个任务也无法及时得到执行。
尝试修改代码,把cancel任务放到独立的线程池去执行:
Executors.newSingleThreadScheduledExecutor().schedule(() -> scheduleAtFixedRateTotalFuture.cancel(false), 1, TimeUnit.SECONDS);

(或者也可以使用2个线程的定时任务线程池)
可以看到,这次的结果符合预期:

和朱晔一起复习Java并发(一):线程池

ForkJoin线程池

从Java 1.8开始,Executors提供了newWorkStealingPool来获得一个ForkJoin线程池。
从命名可以看到,这是一个工作窃取线程池,传统的线程池具有公共的一个任务队列,在任务很多,任务执行很快(CPU密集型任务)的情况下,会发生比较多的竞争问题,而ForkJoin的话每一个线程都有自己的任务队列,如果自己的队列没有任务的话可以从其它队列窃取任务,这样确保了吞吐的同时,减少了竞争。我们写一段代码来比较一下:

和朱晔一起复习Java并发(一):线程池
@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。测试结果如下:

和朱晔一起复习Java并发(一):线程池
可以看到,很明显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线程上执行了,看看运行结果:

和朱晔一起复习Java并发(一):线程池
结果上也可以看到几个结论:
  • 线程池里的任务出了未处理异常,最终这个异常算是未处理异常,可以由未处理异常进行处理
  • 这个线程会终止,而不是传说的线程池会捕获异常
  • 线程池只能重新创建新的异常来填补空白,这是有代价的

不过这个结论不完全正确,我们尝试把execute改为submit来试试:

@Test
public void test() throws InterruptedException {
    String prefix = "test";
    ExecutorService threadPool = Executors.newFixedThreadPool(1, new ThreadFactoryImpl(prefix));
    List 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);
}

输出结果如下:

和朱晔一起复习Java并发(一):线程池
可以看到这次不一样了,异常会在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());
}

结果如下:

和朱晔一起复习Java并发(一):线程池
这就更证实了之前说的重新创建线程的代价问题。你还可以自己测试一下,定时任务线程池的任务出异常了会怎么样?

代码

本文的所有代码见我的Github Repo:https://github.com/JosephZhu1983/java-concurrent-test
代码的测试基于JDK11

总结

本文我们做了十来个实验,各种测试线程池。总结下来几点:

  • 线程池最好手动创建,不要用系统所谓的预定义的常用线程池
  • 在极端情况下可能需要修改默认线程池创建线程的行为
  • 注意线程池中的任务建议还是不要出现未处理异常为好
  • 使用定时任务线程池进行任务执行注意线程池大小问题

Original: https://www.cnblogs.com/lovecindywang/p/11182090.html
Author: lovecindywang
Title: 和朱晔一起复习Java并发(一):线程池

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/539220/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 【一知半解】AQS

    什么是AbstractQueuedSynchronizer(AQS) 字面意思是 &#x62BD;&#x8C61;&#x961F;&#x5217;&…

    Java 2023年6月9日
    088
  • scrapy框架之创建项目运行爬虫

    创建scrapy scrapy startproject 项目名称 创建蜘蛛(爬虫文件) scrapy genspider 蜘蛛名称 网址 爬取网页(举百度的列子) 编写爬虫文件 …

    Java 2023年6月5日
    0104
  • 一个Golang的REPL工具

    REPL为Read-Eval-Print Loop的简写,为一种简易的,可交互式的编程环境,使用者可以方便的调试相关代码: Read: 读取用户输入;Eval: 计算输入的数据;P…

    Java 2023年6月16日
    081
  • Nginx增加网页认证功能

    Nginx增加网页认证功能 增加认证功能模块 ngx_http_auth_basic_module 模块实现让访问者,只有输入正确的用户密码才允许访问web内容。web上的一些内容…

    Java 2023年6月8日
    099
  • EMQX 入门实战(1)–安装及简单使用

    EMQX 是基于 Erlang/OTP 平台开发的开源物联网 MQTT 消息服务器,本文主要介绍其安装及简单使用,文中使用到的软件版本:emqx 4.4.2、Centos 7. 1…

    Java 2023年6月16日
    075
  • [Java]ArrayList源码解析

    ArrayList源码解析 1. 核心源码解读 package java.util; import java.util.function.Consumer; import java…

    Java 2023年6月5日
    072
  • JS 常用语法

    工具 JS在线编辑器,可以临时验证一些想法,非常方便https://jsrun.net/new 常用方法 时间格式排序 var arr=["2022-5-11"…

    Java 2023年6月8日
    053
  • 操作系统 分页式存储管理 实验(C语言)

    分页式存储管理 实验内容 在进程控制实验的基础上实现分页式存储管理内存分配和地址转换过程。进一步实现请求分页式存储管理过程,包括内存和置换空间管理、地址转换以及缺页处理,能够体现F…

    Java 2023年6月5日
    0126
  • 如何快速查看Linux日志?

    因为在生产环境会遇到很多问题,那么最快的定位方式莫过于去看日志,我们都知道服务器每天会产生大量的日志,那么如何快速的定位也就是最关键的。 本文介绍六种查看日志的命令: tail、 …

    Java 2023年6月6日
    0106
  • redis 基于SpringBoot Reids 的工具类

    redis 基于SpringBoot Reids 的工具类 package com.mhy.springredis.utils; import org.springframewor…

    Java 2023年6月16日
    081
  • Java基础随笔

    1.一些简单的dos命令: – d: 回车 盘符切换 – dir(directory):列出当前目录下的文件以及文件夹 – del:删除文件 – md:创建文件夹 – rd:删除文…

    Java 2023年6月5日
    077
  • 看了这么多年西游记,你可知道孙悟空是如何召唤土地公公的吗?

    小时候最开心的事莫过于躺在沙发上看《西游记》了。大闹天宫、三打白骨精、真假美猴王……一幕幕精彩的故事萦绕脑海,现在想来,回味无穷。 不知道你有没有注意到这个…

    Java 2023年6月5日
    086
  • Mybatisi和Spring整合源码分析

    一、MybatisSpring的使用 1.创建 Maven 工程。 2.添加依赖,代码如下 org.mybatis mybatis 3.5.7-ybe org.mybatis my…

    Java 2023年6月13日
    093
  • Netty源码分析之ChannelPipeline(五)—异常事件的传播

    ChannelHandler中异常的获取与处理是通过继承重写exceptionCaught方法来实现的,本篇文章我们对ChannelPipeline中exceptionCaught…

    Java 2023年6月9日
    081
  • ApplicationContext refresh 过程及一些重要的 processor 解析

    注册 Bean 到容器,通过限定名获取 Bean 可以拦截 Bean 初始化前后的处理 可以在 Bean 属性注入后和即将销毁时做一些逻辑处理 解决了循环依赖 其实总结起来它实现的…

    Java 2023年6月5日
    078
  • Jetpack Compose学习(9)——Compose中的列表控件(LazyRow和LazyColumn)

    原文:Jetpack Compose学习(9)——Compose中的列表控件(LazyRow和LazyColumn) – Stars-One的杂货小窝 经过前面的学习,…

    Java 2023年6月13日
    082
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球