JAVA线程池之ThreadPoolExecutor

ThreadPoolExecutor的七大参数

  • @param corePoolSize the number of threads to keep in the pool, even* if they are idle, unless {@code allowCoreThreadTimeOut} isset 池中一直保持的线程的数量,即使线程空闲。除非设置了 allowCoreThreadTimeOut
  • @param maximumPoolSize the maximum number of threads to allow inthe* pool 池中允许的最大的线程数
  • @param keepAliveTime when the number of threads is greater than* the core, this is the maximumtime that excess idle threads* will wait for new tasks before terminating. 当线程数大于核心线程数的时候,线程在最大多长时间没有接到新任务就会终止释放,最终线程池维持在 corePoolSize 大小
  • @param unit the time unit for the {@code keepAliveTime} argument时间单位
  • @param workQueue the queue to use for holding tasks before theyare* executed. This queue will hold only the {@code Runnable}* tasks submitted by the {@code execute} method. 阻塞队列,用来存储等待执行的任务,如果当前对线程的需求超过了corePoolSize大小,就会放在这里等待空闲线程执行。
  • @param threadFactory the factory to use when the executor creates a new thread创建线程的工厂,比如指定线程名等
    image
/**
 * 七大参数
 * corePoolSize:[5] 核心线程数[一直存在除非(allowCoreThreadTimeOut)];  线程池,创建好以后就准备就绪的线程数量,就等待来接受异步任务去执行。
 * 		5个  Thread thread = new Thread();  thread.start();
 * maximumPoolSize:[200] 最大线程数量;  控制资源
 * keepAliveTime: 存活时间。如果当前的线程数量大于 core 数量。
 * 		释放空闲的线程(maximumPoolSize-corePoolSize)。只要线程空闲大于指定的keepAliveTime;
 * unit:时间单位
 * BlockingQueue<Runnable> workQueue:阻塞队列。如果任务有很多,就会将目前多的任务放在队列里面。只要有线程空闲,就会去队列里面取出新的任务继续执行。
 * threadFactory:线程的创建工厂。
 * RejectedExecutionHandler handler:如果队列满了,按照我们指定的拒绝策略拒绝执行任务
 */

运行流程

  • 1、线程池创建,准备好 core 数量的核心线程,准备接受任务

  • 2、新的任务进来,用 core 准备好的空闲线程执行。

    • (1) 、core 满了,就将再进来的任务放入阻塞队列中。空闲的core 就会自己去阻塞队列获取任务执行
    • (2) 、阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量
    • (3) 、max 都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自动销毁。最终保持到 core 大小
    • (4) 、如果线程数开到了 max 的数量,还有新任务进来,就会使用reject 指定的拒绝策略进行处理
  • 3、所有的线程创建都是由指定的 factory 创建的。

总结

简单来说就是多个线程进来先看核心线程数够不够,如果不够进入等待队列,队列满了就用max线程数的数量,还是不够使用淘汰策略。

    /**
     * 
   
//    new Thread(()-> System.out.println("hello")).start();

   
     * 工作顺序:
     * 1)、线程池创建,准备好core数量的核心线程,准备接受任务
     * 1.1、core满了,就将再进来的任务放入阻塞队列中。空闲的core就会自己去阻塞队列获取任务执行
     * 1.2、阻塞队列满了,就直接开新线程执行,最大只能开到max指定的数量
     * 1.3、max满了就用RejectedExecutionHandler拒绝任务
     * 1.4、max都执行完成,有很多空闲.在指定的时间keepAliveTime以后,释放max-core这些线程
     *
     *      new LinkedBlockingDeque<>():默认是Integer的最大值。内存不够
     *
     * 一个线程池 core 7; max 20 ,queue:50,100并发进来怎么分配的;
     * 7个会立即得到执行,50个会进入队列,再开13个进行执行。剩下的30个就使用拒绝策略。
     * 如果不想抛弃还要执行。CallerRunsPolicy;
     *
     */
	ThreadPoolExecutor executor = new ThreadPoolExecutor(5,
        200,
        10,
        TimeUnit.SECONDS,
        new LinkedBlockingDeque<>(100000),
        Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.AbortPolicy());

面试

一个线程池 core 7; max 20 ,queue:50,100 并发进来怎么分配的?
先有 7 个能直接得到执行,接下来 50个进入队列排队,在多开 13 个继续执行。现在70 个被安排上了。剩下 30 个默认拒绝策略。

扩展

使用Excutors包下的几个常用创建线程池方法
常见的 4 种线程池

  • A、newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

  • B、newFixedThreadPool

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

  • C、newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行。

  • D、newSingleThreadExecutor

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序 (FIFO、LIFO、优先级) 执行。

//  Executors.newFixedThreadPool() 固定大小,core=max;都不可回收
//  Executors.newScheduledThreadPool() 定时任务的线程池
//  Executors.newSingleThreadExecutor() 单线程的线程池,后台从队列里面获取任务,挨个执行

但是阿里JAVA开发规范并不推荐这么使用,推荐使用ThreadPoolExecutor这种方式
Executors部分方法的弊端:

newFixedThreadPool和newSingleThreadExecutor主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
newCachedThreadPool和newScheduledThreadPool:主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

同时,阿里巴巴java开发规范中推荐了3种线程池创建方式。

  • 方式一,引入commons-lang3包。
//org.apache.commons.lang3.concurrent.BasicThreadFactory
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
    new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());
  • 方式二,引入com.google.guava包。
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
    .setNameFormat("demo-pool-%d").build();

//Common Thread Pool
ExecutorService pool = new ThreadPoolExecutor(5, 200,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

pool.execute(()-> System.out.println(Thread.currentThread().getName()));
pool.shutdown();//gracefully shutdown
  • 方式三,spring配置线程池方式:自定义线程工厂bean需要实现ThreadFactory,可参考该接口的其它默认实现类,使用方式直接注入bean,调用execute(Runnable task)方法即可。
<bean  
    class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="10" />
    <property name="maxPoolSize" value="100" />
    <property name="queueCapacity" value="2000" />

<property name="threadFactory" value= threadFactory />
    <property name="rejectedExecutionHandler">
        <ref local="rejectedExecutionHandler" />
    </property>
</bean>

// in code
userThreadPool.execute(thread);

posted @ 2022-11-29 09:51  长情c  阅读(134)  评论(0)    收藏  举报