• 浅析常见的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

  • 相关阅读:
    pku 2446 Chessboard
    POJ 2724 Purifying Machine
    14 Flash内存控制器(FMC)
    10 PWM发生器和捕捉定时器
    15 I2C总线控制器
    18 RTX Kernel实时系统
    17模拟数字转换
    19.1 详解startup_M051.s
    16串行外围设备接口(SPI)控制器
    13看门狗
  • 原文地址:https://www.cnblogs.com/goloving/p/15067540.html
Copyright © 2020-2023  润新知