Executors工厂方法的使用

Executors提供了四个创建线程池的工厂方法,分别是:

  • Executors.newSingleThreadExecutor()
  • Executors.newFixedThreadPool()
  • Executors.newCachedThreadPool()
  • Executors.newScheduledThreadPool()

newSingleThreadExecutor()

newSingleThreadExecutor()只会启用一个核心线程干活,使用无界队列存放所有的任务。

package com.morris.concurrent.threadpool.threadpoolexecutor.executors;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

import lombok.extern.slf4j.Slf4j;

/**
 * newSingleThreadExecutor的使用
 * 只有一个线程干活
 */
@Slf4j
public class SingleThreadPoolDemo {

    public static void main(String[] args) {

        ExecutorService executorService = Executors.newSingleThreadExecutor();

        IntStream.rangeClosed(1, 3).forEach(i -> executorService.submit(() -> {
            log.info("task begin ...");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("task end .");
        }));
        executorService.shutdown();
    }
}

运行结果如下:

2020-10-14 10:42:54,971  INFO [pool-1-thread-1] (SingleThreadPoolDemo.java:18) - task begin ...
2020-10-14 10:42:57,973  INFO [pool-1-thread-1] (SingleThreadPoolDemo.java:24) - task end .
2020-10-14 10:42:57,973  INFO [pool-1-thread-1] (SingleThreadPoolDemo.java:18) - task begin ...
2020-10-14 10:43:00,975  INFO [pool-1-thread-1] (SingleThreadPoolDemo.java:24) - task end .
2020-10-14 10:43:00,975  INFO [pool-1-thread-1] (SingleThreadPoolDemo.java:18) - task begin ...
2020-10-14 10:43:03,975  INFO [pool-1-thread-1] (SingleThreadPoolDemo.java:24) - task end .

newSingleThreadExecutor()的源码如下,可以看出只有一个核心线程,使用阻塞队列的是无界队列。

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

这里有个疑问,一个线程还搞啥线程池,直接new Thread()岂不是更简单明了?

下面通过一个例子来说明newSingleThreadExecutor()线程池与new Thread()的区别:

package com.morris.concurrent.threadpool.threadpoolexecutor.executors;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * newSingleThreadExecutor的使用
 * 只有一个线程干活
 */
@Slf4j
public class SingleThreadPoolDemo2 {

    public static void main(String[] args) {

        ExecutorService executorService = Executors.newSingleThreadExecutor();

        IntStream.rangeClosed(1, 5).forEach(i -> executorService.submit(() -> {
            log.info("task{} begin ...", i);
            if (i == 3) {
                int j = 10 / 0;
            }
            log.info("task{} end .", i);
        }));
        executorService.shutdown();
    }
}

运行结果如下:

2020-10-15 14:19:52,309  INFO [pool-1-thread-1] (SingleThreadPoolDemo2.java:22) - task1 begin ...
2020-10-15 14:19:52,312  INFO [pool-1-thread-1] (SingleThreadPoolDemo2.java:26) - task1 end .
2020-10-15 14:19:52,314  INFO [pool-1-thread-1] (SingleThreadPoolDemo2.java:22) - task2 begin ...
2020-10-15 14:19:52,314  INFO [pool-1-thread-1] (SingleThreadPoolDemo2.java:26) - task2 end .
2020-10-15 14:19:52,314  INFO [pool-1-thread-1] (SingleThreadPoolDemo2.java:22) - task3 begin ...
2020-10-15 14:19:52,315  INFO [pool-1-thread-1] (SingleThreadPoolDemo2.java:22) - task4 begin ...
2020-10-15 14:19:52,315  INFO [pool-1-thread-1] (SingleThreadPoolDemo2.java:26) - task4 end .
2020-10-15 14:19:52,315  INFO [pool-1-thread-1] (SingleThreadPoolDemo2.java:22) - task5 begin ...
2020-10-15 14:19:52,315  INFO [pool-1-thread-1] (SingleThreadPoolDemo2.java:26) - task5 end .

自己创建一个单线程串行执行任务,如果任务执行失败而终止那么没有任何补救措施,而线程池会将异常捕获存放到FutureTask中,然后继续执行下一个任务,当调用Future.get()时才会抛出异常,不会影响下一个任务的执行。

newFixedThreadPool()

newSingleThreadExecutor()使用固定个数的核心线程干活,使用无界队列存放所有的任务。

package com.morris.concurrent.threadpool.threadpoolexecutor.executors;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * newFixedThreadPool的使用
 * 定长,指定多少个,就启动多少个线程干活,无界队列存储所有的任务
 */
@Slf4j
public class FixThreadPoolDemo {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        IntStream.rangeClosed(1, 10).forEach(i -> executorService.submit(() -> {
            log.info("task{} begin ...", i);
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("task{} end .", i);
        }));
        executorService.shutdown();
    }
}

运行结果如下:

2020-10-14 11:03:41,065  INFO [pool-1-thread-3] (FixThreadPoolDemo.java:17) - task3 begin ...
2020-10-14 11:03:41,065  INFO [pool-1-thread-1] (FixThreadPoolDemo.java:17) - task1 begin ...
2020-10-14 11:03:41,065  INFO [pool-1-thread-2] (FixThreadPoolDemo.java:17) - task2 begin ...
2020-10-14 11:03:44,067  INFO [pool-1-thread-3] (FixThreadPoolDemo.java:23) - task3 end .
2020-10-14 11:03:44,067  INFO [pool-1-thread-1] (FixThreadPoolDemo.java:23) - task1 end .
2020-10-14 11:03:44,067  INFO [pool-1-thread-3] (FixThreadPoolDemo.java:17) - task4 begin ...
2020-10-14 11:03:44,067  INFO [pool-1-thread-1] (FixThreadPoolDemo.java:17) - task5 begin ...
2020-10-14 11:03:44,068  INFO [pool-1-thread-2] (FixThreadPoolDemo.java:23) - task2 end .
2020-10-14 11:03:44,068  INFO [pool-1-thread-2] (FixThreadPoolDemo.java:17) - task6 begin ...
2020-10-14 11:03:47,067  INFO [pool-1-thread-1] (FixThreadPoolDemo.java:23) - task5 end .
2020-10-14 11:03:47,067  INFO [pool-1-thread-3] (FixThreadPoolDemo.java:23) - task4 end .
2020-10-14 11:03:47,067  INFO [pool-1-thread-1] (FixThreadPoolDemo.java:17) - task7 begin ...
2020-10-14 11:03:47,067  INFO [pool-1-thread-3] (FixThreadPoolDemo.java:17) - task8 begin ...
2020-10-14 11:03:47,068  INFO [pool-1-thread-2] (FixThreadPoolDemo.java:23) - task6 end .
2020-10-14 11:03:47,068  INFO [pool-1-thread-2] (FixThreadPoolDemo.java:17) - task9 begin ...
2020-10-14 11:03:50,067  INFO [pool-1-thread-3] (FixThreadPoolDemo.java:23) - task8 end .
2020-10-14 11:03:50,068  INFO [pool-1-thread-3] (FixThreadPoolDemo.java:17) - task10 begin ...
2020-10-14 11:03:50,068  INFO [pool-1-thread-2] (FixThreadPoolDemo.java:23) - task9 end .
2020-10-14 11:03:50,067  INFO [pool-1-thread-1] (FixThreadPoolDemo.java:23) - task7 end .
2020-10-14 11:03:53,068  INFO [pool-1-thread-3] (FixThreadPoolDemo.java:23) - task10 end .

newSingleThreadExecutor()的源码如下,可以看出核心线程的个数为传入的 nThreads,使用的阻塞队列为无界队列:

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

newCachedThreadPool()

newCachedThreadPool()中每提交一个任务,如果没有空闲的线程,就会一直创建新的线程来执行任务。

package com.morris.concurrent.threadpool.threadpoolexecutor.executors;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * newCachedThreadPool的使用
 * 来一个任务,如果没有空闲的线程,就会一直创建线程来执行任务
 */
@Slf4j
public class CachedThreadPoolDemo {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();

        IntStream.rangeClosed(1, 10).forEach(i -> executorService.submit(() -> {
            log.info("task{} begin ...", i);
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("task{} end .", i);
        }));
        executorService.shutdown();
    }
}

运行结果如下:

2020-10-14 11:11:06,416  INFO [pool-1-thread-1] (CachedThreadPoolDemo.java:20) - task1 begin ...
2020-10-14 11:11:06,417  INFO [pool-1-thread-9] (CachedThreadPoolDemo.java:20) - task9 begin ...
2020-10-14 11:11:06,417  INFO [pool-1-thread-5] (CachedThreadPoolDemo.java:20) - task5 begin ...
2020-10-14 11:11:06,417  INFO [pool-1-thread-6] (CachedThreadPoolDemo.java:20) - task6 begin ...
2020-10-14 11:11:06,417  INFO [pool-1-thread-7] (CachedThreadPoolDemo.java:20) - task7 begin ...
2020-10-14 11:11:06,417  INFO [pool-1-thread-8] (CachedThreadPoolDemo.java:20) - task8 begin ...
2020-10-14 11:11:06,417  INFO [pool-1-thread-3] (CachedThreadPoolDemo.java:20) - task3 begin ...
2020-10-14 11:11:06,417  INFO [pool-1-thread-10] (CachedThreadPoolDemo.java:20) - task10 begin ...
2020-10-14 11:11:06,416  INFO [pool-1-thread-4] (CachedThreadPoolDemo.java:20) - task4 begin ...
2020-10-14 11:11:06,416  INFO [pool-1-thread-2] (CachedThreadPoolDemo.java:20) - task2 begin ...
2020-10-14 11:11:09,418  INFO [pool-1-thread-1] (CachedThreadPoolDemo.java:26) - task1 end .
2020-10-14 11:11:09,418  INFO [pool-1-thread-9] (CachedThreadPoolDemo.java:26) - task9 end .
2020-10-14 11:11:09,419  INFO [pool-1-thread-5] (CachedThreadPoolDemo.java:26) - task5 end .
2020-10-14 11:11:09,420  INFO [pool-1-thread-7] (CachedThreadPoolDemo.java:26) - task7 end .
2020-10-14 11:11:09,420  INFO [pool-1-thread-10] (CachedThreadPoolDemo.java:26) - task10 end .
2020-10-14 11:11:09,420  INFO [pool-1-thread-3] (CachedThreadPoolDemo.java:26) - task3 end .
2020-10-14 11:11:09,420  INFO [pool-1-thread-4] (CachedThreadPoolDemo.java:26) - task4 end .
2020-10-14 11:11:09,420  INFO [pool-1-thread-6] (CachedThreadPoolDemo.java:26) - task6 end .
2020-10-14 11:11:09,420  INFO [pool-1-thread-2] (CachedThreadPoolDemo.java:26) - task2 end .
2020-10-14 11:11:09,420  INFO [pool-1-thread-8] (CachedThreadPoolDemo.java:26) - task8 end .

先来理解下一个特殊的阻塞队列SynchronousQueue:

package com.morris.concurrent.threadpool.threadpoolexecutor.blockqueue;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

/**
 * SynchronousQueue的使用
 */
@Slf4j
public class SynchronousQueueDemo {

    public static void main(String[] args) {
        SynchronousQueue synchronousQueue = new SynchronousQueue();

        Thread produceThread = new Thread(() -> {
            log.info("begin...");
            try {
                TimeUnit.SECONDS.sleep(10);
                synchronousQueue.put("ooxx"); // 阻塞
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("end.");
        }, "produceThread");
        produceThread.start();


        Thread consumerThread = new Thread(() -> {
            log.info("begin...");
            try {
                log.info("get message:{}", synchronousQueue.take()); // 阻塞
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("end.");
        }, "consumerThread");
        consumerThread.start();
    }
}

运行结果如下:

2020-10-14 14:49:13,158  INFO [produceThread] (SynchronousQueueDemo.java:18) - begin...
2020-10-14 14:49:13,158  INFO [consumerThread] (SynchronousQueueDemo.java:31) - begin...
2020-10-14 14:49:23,160  INFO [produceThread] (SynchronousQueueDemo.java:25) - end.
2020-10-14 14:49:23,161  INFO [consumerThread] (SynchronousQueueDemo.java:33) - get message:ooxx
2020-10-14 14:49:23,161  INFO [consumerThread] (SynchronousQueueDemo.java:37) - end.

从运行结果可以发现,一个线程调用take()方法时如果阻塞队列中没有另一个线程来调用put方法时就会一直被阻塞,可以认为这是一种线程与线程间一对一传递消息的模型。同样,如果一个线程调用了put()方法生产了数据放入阻塞队列中,如果没有另一个线程来调用take()方法从阻塞队列中消费数据,put就会一直被阻塞直到有线程调用take()方法。

newCachedThreadPool()的源码如下:

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

newCachedThreadPool()线程池中没有核心线程数,当一个任务提交过来时,如果有线程池中没有空闲的线程,而SynchronousQueue是无法存放数据的,所以会创建一个新的线程来执行这个任务,当线程池中的线程60s内还没有获取到任务就会自动销毁。

适用场景:这种线程池比较灵活,适用于消费者的能力和生产者的能力相等,也就是需要异步执行并且执行时间短的任务,不适用于那种生产者生产数据非常快,消费消费数据非常慢,甚至有大量的IO阻塞的任务,如果消费者不能及时消费数据,那么就会不停的创建线程,最后会导致OOM异常。

newScheduledThreadPool()

newScheduledThreadPool()可以让提交的任务延迟执行。

package com.morris.concurrent.threadpool.threadpoolexecutor.executors;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * ScheduledThreadPool的使用
 */
@Slf4j
public class ScheduledThreadPoolDemo {

    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);

        IntStream.rangeClosed(1, 5).forEach(i -> {
            log.info("submit task{}", i);
            scheduledExecutorService.schedule(() -> {
                log.info("execute task{}", i);
            }, 3L, TimeUnit.SECONDS);
        });
        scheduledExecutorService.shutdown();
    }
}

运行结果如下:

2020-10-15 11:03:37,110  INFO [main] (ScheduledThreadPoolDemo.java:20) - submit task1
2020-10-15 11:03:37,114  INFO [main] (ScheduledThreadPoolDemo.java:20) - submit task2
2020-10-15 11:03:37,115  INFO [main] (ScheduledThreadPoolDemo.java:20) - submit task3
2020-10-15 11:03:37,115  INFO [main] (ScheduledThreadPoolDemo.java:20) - submit task4
2020-10-15 11:03:37,116  INFO [main] (ScheduledThreadPoolDemo.java:20) - submit task5
2020-10-15 11:03:40,115  INFO [pool-1-thread-2] (ScheduledThreadPoolDemo.java:22) - execute task2
2020-10-15 11:03:40,115  INFO [pool-1-thread-1] (ScheduledThreadPoolDemo.java:22) - execute task1
2020-10-15 11:03:40,115  INFO [pool-1-thread-3] (ScheduledThreadPoolDemo.java:22) - execute task3
2020-10-15 11:03:40,117  INFO [pool-1-thread-2] (ScheduledThreadPoolDemo.java:22) - execute task4
2020-10-15 11:03:40,117  INFO [pool-1-thread-1] (ScheduledThreadPoolDemo.java:22) - execute task5

从运行结果的执行时间上可以看出,提交任务后,等待3s,任务才会被执行。

如果我想任务每隔3s钟执行一次,该怎么实现呢?

package com.morris.concurrent.threadpool.threadpoolexecutor.executors;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * ScheduledThreadPool的使用
 * 每隔3秒执行一次任务
 */
@Slf4j
public class ScheduledThreadPoolDemo2 {

    private static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);

    public static void main(String[] args) {
        execute();
    }

    private static void execute() {
        scheduledExecutorService.schedule(() -> {
            log.info("execute task");
            execute();
        }, 3, TimeUnit.SECONDS);

    }
}

咋一看上面的代码,好像是一个递归的死循环,会因为方法的调用次数过多,导致线程的虚拟机栈的栈帧过多,从而导致OOM异常,其实execute()的是由线程池中的线程异步执行,所以不会OOM,从运行结果也可以发现任务会由不同的线程运行。

运行结果如下:

2020-10-15 11:23:21,301  INFO [pool-1-thread-1] (ScheduledThreadPoolDemo2.java:23) - execute task
2020-10-15 11:23:24,305  INFO [pool-1-thread-1] (ScheduledThreadPoolDemo2.java:23) - execute task
2020-10-15 11:23:27,307  INFO [pool-1-thread-2] (ScheduledThreadPoolDemo2.java:23) - execute task
2020-10-15 11:23:30,308  INFO [pool-1-thread-2] (ScheduledThreadPoolDemo2.java:23) - execute task
... ...

当然newScheduledThreadPool()已经封装了一个现成的方法scheduleWithFixedDelay来实现上面类似递归的功能。

package com.morris.concurrent.threadpool.threadpoolexecutor.executors;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * ScheduledThreadPool的使用
 * 每隔3秒执行一次任务之scheduleWithFixedDelay
 */
@Slf4j
public class ScheduledThreadPoolDemo3 {

    private static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);

    public static void main(String[] args) {
        scheduledExecutorService.scheduleWithFixedDelay(() -> {
            log.info("execute task");
            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 3,5, TimeUnit.SECONDS);
    }
}

运行结果如下:

2020-10-15 11:36:55,733  INFO [pool-1-thread-1] (ScheduledThreadPoolDemo3.java:20) - execute task
2020-10-15 11:37:10,736  INFO [pool-1-thread-1] (ScheduledThreadPoolDemo3.java:20) - execute task
2020-10-15 11:37:25,739  INFO [pool-1-thread-2] (ScheduledThreadPoolDemo3.java:20) - execute task
2020-10-15 11:37:40,739  INFO [pool-1-thread-2] (ScheduledThreadPoolDemo3.java:20) - execute task
... ...

上面的代码也会有一个问题:假如任务从00:00:00时间点开始执行,任务执行需要花费10s,那么下一个任务在00:00:10才会被提交到线程池中,真正的执行时间点在00:00:13,而不是每个3s的时间点00:00:03

newScheduledThreadPool()提供了一个固定时间间隔执行任务的方法:

package com.morris.concurrent.threadpool.threadpoolexecutor.executors;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * ScheduledThreadPool的使用
 * 每隔3秒执行一次任务之scheduleAtFixedRate
 */
@Slf4j
public class ScheduledThreadPoolDemo4 {

    private static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);

    public static void main(String[] args) {
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            log.info("execute task");
            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 3, 5, TimeUnit.SECONDS);
    }
}

运行结果如下:

2020-10-15 13:46:50,591  INFO [pool-1-thread-1] (ScheduledThreadPoolDemo4.java:20) - execute task
2020-10-15 13:47:00,593  INFO [pool-1-thread-1] (ScheduledThreadPoolDemo4.java:20) - execute task
2020-10-15 13:47:10,596  INFO [pool-1-thread-2] (ScheduledThreadPoolDemo4.java:20) - execute task
2020-10-15 13:47:20,596  INFO [pool-1-thread-2] (ScheduledThreadPoolDemo4.java:20) - execute task
... ...

从运行结果发现,任务执行的时间间隔为10s,并不是参数设置的5s,也就说当执行任务的时间大于我们指定的间隔时间时,它并不会在指定间隔时开辟一个新的线程并发执行这个任务,而是等待该线程执行完毕。

scheduleAtFixedRate()的源码注释上面也对此方法的使用进行了说明:

/**
 * Creates and executes a periodic action that becomes enabled first
 * after the given initial delay, and subsequently with the given
 * period; that is executions will commence after
 * {@code initialDelay} then {@code initialDelay+period}, then
 * {@code initialDelay + 2 * period}, and so on.
 * If any execution of the task
 * encounters an exception, subsequent executions are suppressed.
 * Otherwise, the task will only terminate via cancellation or
 * termination of the executor.  If any execution of this task
 * takes longer than its period, then subsequent executions
 * may start late, but will not concurrently execute.
......

注释中还提到了如果任务发生了异常,后面的任务就不会执行了。

package com.morris.concurrent.threadpool.threadpoolexecutor.executors;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * ScheduledThreadPool的使用
 * 每隔3秒执行一次任务之异常,任务发生异常后,后面的任务就不会执行了
 */
@Slf4j
public class ScheduledThreadPoolDemo5 {

    private static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);

    private static int i = 0;

    public static void main(String[] args) {
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            log.info("execute task");
            if(i == 3) {
                int j = i / 0;
            }
            i++;
        }, 3, 5, TimeUnit.SECONDS);
    }
}

运行结果如下,只执行4次就不会执行了,但是进程还在,因为还有活跃的非守护线程,异常不会抛出来。

2020-10-15 14:11:14,201  INFO [pool-1-thread-1] (ScheduledThreadPoolDemo5.java:22) - execute task
2020-10-15 14:11:19,202  INFO [pool-1-thread-1] (ScheduledThreadPoolDemo5.java:22) - execute task
2020-10-15 14:11:24,202  INFO [pool-1-thread-2] (ScheduledThreadPoolDemo5.java:22) - execute task
2020-10-15 14:11:29,202  INFO [pool-1-thread-2] (ScheduledThreadPoolDemo5.java:22) - execute task

最后再来看一下newScheduledThreadPool()的源码:

java.util.concurrent.Executors#newScheduledThreadPool(int)

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

java.util.concurrent.ScheduledThreadPoolExecutor#ScheduledThreadPoolExecutor(int)

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

newScheduledThreadPool()不像其他3个线程池直接创建ThreadPoolExecutor对象,而是创建一个继承了ThreadPoolExecutor的ScheduledThreadPoolExecutor对象,另外还使用了带延迟的阻塞队列DelayedWorkQueue。

总结

最后用一个表格来总结本文的内容:

线程池

创建的对象

核心线程数

最大线程数据

超时时间

超时时间单位

阻塞队列

newSingleThreadExecutor

ThreadPoolExecutor

1

1

0

MILLISECONDS

LinkedBlockingQueue

newFixedThreadPool()

ThreadPoolExecutor

n

n

0

MILLISECONDS

LinkedBlockingQueue

newCachedThreadPool()

ThreadPoolExecutor

0

Integer.MAX_VALUE

60

SECONDS

SynchronousQueue

newScheduledThreadPool()

ScheduledThreadPoolExecutor

n

Integer.MAX_VALUE

0

NANOSECONDS

DelayedWorkQueue