浅析常见的4种线程池及Spring集合的线程处理类ThreadPoolTaskExecutor原因及其用法

一、四种常见的线程池

  CachedThreadPool:可缓存的线程池,该线程池中没有核心线程,非核心线程的数量为Integer.max_value,就是无限大,当有需要时创建线程来执行任务,没有需要时回收线程,适用于耗时少,任务量大的情况。

  SecudleThreadPool:周期性执行任务的线程池,按照某种特定的计划执行线程中的任务,有核心线程,但也有非核心线程,非核心线程的大小也为无限大。适用于执行周期性的任务。

  SingleThreadPool:只有一条线程来执行任务,适用于有顺序的任务的应用场景。

  FixedThreadPool:定长的线程池,有核心线程,核心线程的即为最大的线程数量,没有非核心线程

二、JUC 包的 ExecutorService 接口创建 4 种线程池

  要说ThreadPoolTaskExecutor这个类,先要从java本身的JUC包讲起,我们首先要知道这一点,java中所有对线程池的管理都要遵循一个接口即ExecutorService,这个接口为我们使用线程池定义了一些规则。

  在JUC(java.util.concurrent)包中为我们提供了Executors的静态方法创建几种不同的线程池来使用

    /**
    创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
    */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    /**
    创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    /**
    创建一个定长线程池,支持定时及周期性任务执行。
    */
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    @see java.util.concurrent.ScheduledThreadPoolExecutor
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
    @see java.util.concurrent.ThreadPoolExecutor
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
    /**
    创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
    */
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

  从源码上可以看出,这些创建线程池的方式最终都是调用了ThreadPoolExecutor类的构造方法返回了一个ExecutorService实例。

  这里的ThreadPoolExecutor是一个比较重要的线程池实现类

public class ThreadPoolExecutor extends AbstractExecutorService {
    ...
    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;
    ...
    private volatile int corePoolSize;
    private volatile int maximumPoolSize;
    private volatile long keepAliveTime;
    private static final RejectedExecutionHandler defaultHandler =new AbortPolicy();
    private final BlockingQueue<Runnable> workQueue;
    ...
        
}

  ThreadPoolExecutor类中定义了我们所熟悉的线程池的生命周期,还有几个核心的参数;回到正题,ThreadPoolExecutor跟我们所要讲的ThreadPoolTaskExecutor有何关系呢?上面说到,ThreadPoolExecutor是JUC包中提供给我们管理和使用线程池的实现类,且常常使用Executor工具类的几种静态方法进行实例化使用,但是请注意我们在使用的时候并不是想用线程池就使用诸如Executors.newCachedThreadPool()来创建一个的,这样已经违背线程池出现的原因,即管理宝贵的线程资源。

三、Spring集成的ThreadPoolTaskExecutor —— 单例

  Spring框架中为我们封装了一个类,就是今天的主角ThreadPoolTaskExecutor,实际在使用线程池时我们就使用这个类实现,看源码:

public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport implements AsyncListenableTaskExecutor, SchedulingTaskExecutor {
    private final Object poolSizeMonitor = new Object();
    private int corePoolSize = 1;
    private int maxPoolSize = 2147483647;
    private int keepAliveSeconds = 60;
    private int queueCapacity = 2147483647;
    private boolean allowCoreThreadTimeOut = false;
    @Nullable
    private TaskDecorator taskDecorator;
    @Nullable
    private ThreadPoolExecutor threadPoolExecutor;
    
}

  可以看到ThreadPoolTaskExecutor类中维护了一个ThreadPoolExecutor成员变量,此中的端倪就在这里的ThreadPoolExecutor变量。那ThreadPoolTaskExecutor实际开发中是怎么使用的呢?

1、步骤一、配置Bean

@Configuration
public class ThreadPoolTaskExecutorConfig {
    @Bean("threadPoolTaskExecutor")
    ThreadPoolTaskExecutor getThreadPoolTaskExecutor(){
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(3);
        threadPoolTaskExecutor.setMaxPoolSize(10);
        threadPoolTaskExecutor.setQueueCapacity(200);
        threadPoolTaskExecutor.setThreadNamePrefix("task-concurrent-work");
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 这一步是不需要的,下面会讲解到
        // threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }
}

2、步骤二、使用时直接使用@AutoWired注解注入使用

@Autowired 
ThreadPoolTaskExecutor threadPoolTaskExecutor;

...
threadPoolTaskExecutor.execute(() -> System.out.println("执行了线程工作"));
...

  线程池ThreadPoolTaskExecutor配置说明,这篇文章也可以看看,具体使用描述。

3、上面讲到使用配置Bean的方式将ThreadPoolTaskExecutor交给spring进行实例化,在实例化中不需要进行 threadPoolTaskExecutor.initialize(); 这一步操作,为什么呢?

  通过看源码我们看到ThreadPoolTaskExecutor类(实际上是其父类)实现了InitializingBean接口,则在spring容器初始化时通过父类实现的afterPropertiesSet()方法最终触发ThreadPoolTaskExecutor中的initializeExecutor方法从而this.threadPoolExecutor = executor;将成员变量中的ThreadPoolExecutor实例化。

protected ExecutorService initializeExecutor(ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
        BlockingQueue<Runnable> queue = this.createQueue(this.queueCapacity);
        ThreadPoolExecutor executor;
        if (this.taskDecorator != null) {
            executor = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, (long)this.keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler) {
                public void execute(Runnable command) {
                    Runnable decorated = ThreadPoolTaskExecutor.this.taskDecorator.decorate(command);
                    if (decorated != command) {
                        ThreadPoolTaskExecutor.this.decoratedTaskMap.put(decorated, command);
                    }

                    super.execute(decorated);
                }
            };
        } else {
            executor = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, (long)this.keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler);
        }

        if (this.allowCoreThreadTimeOut) {
            executor.allowCoreThreadTimeOut(true);
        }

        this.threadPoolExecutor = executor;
        return executor;
}

  另外可以看出,实际上在ThreadPoolTaskExecutor中最终还是使用了ThreadPoolExecutor的构造方法来实现一个线程池的。

  综上总结一下, 使用ThreadPoolTaskExecutor线程池时,我们需要将他交给spring进行管理(默认创建一个单例),而在使用时直接注入到组件中使用即可,这样就保证不会滥用线程池了。

参考文章:https://blog.csdn.net/qq_41923982/article/details/106268653

原文地址:https://www.cnblogs.com/goloving/p/15067540.html