线程池

作者: Lnstark | 来源:发表于2021-10-22 21:40 被阅读0次

    一、线程池的创建

    1. 创建方式

    Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待;
    Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;
    Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序;
    Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池;
    Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池;
    Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】。
    ThreadPoolExecutor:最原始的创建线程池的方式。

    2. 创建参数

    7 个参数代表的含义如下:

    1. corePoolSize

    核心线程数,线程池中始终存活的线程数。如果任务是IO密集型,一般线程数需要设置2倍CPU数以上,以此来尽量利用CPU资源。如果任务是CPU密集型,一般线程数量只需要设置CPU数加1即可,更多的线程数也只能增加上下文切换,不能增加CPU利用率。

    2. maximumPoolSize

    最大线程数,线程池中允许的最大线程数,当线程池的任务队列满了之后可以创建的最大线程数。

    3. keepAliveTime

    最大线程数可以存活的时间,当线程中没有任务执行时,最大线程就会销毁一部分,最终保持核心线程数量的线程。

    4. unit:

    单位是和参数 3 存活时间配合使用的,合在一起用于设定线程的存活时间 ,参数 keepAliveTime 的时间单位有以下 7 种可选:

    TimeUnit.DAYS:天
    TimeUnit.HOURS:小时
    TimeUnit.MINUTES:分
    TimeUnit.SECONDS:秒
    TimeUnit.MILLISECONDS:毫秒
    TimeUnit.MICROSECONDS:微妙
    TimeUnit.NANOSECONDS:纳秒

    5. workQueue

    一个阻塞队列,用来存储线程池等待执行的任务,均为线程安全,它包含以下 7 种类型:
    ArrayBlockingQueue(有界队列):一个由数组结构组成的有界阻塞队列。
    LinkedBlockingQueue(无界队列):一个由链表结构组成的无界阻塞队列。
    SynchronousQueue(同步队列):一个不存储元素的阻塞队列,即直接提交给线程不保持它们。
    PriorityBlockingQueue(优先阻塞队列):一个支持优先级排序的无界阻塞队列。
    DelayQueue(延迟队列):一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素。
    LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。与SynchronousQueue类似,还含有非阻塞方法。
    LinkedBlockingDeque:一个由链表结构组成的双端阻塞队列。
    较常用的是 LinkedBlockingQueue 和 Synchronous,线程池的排队策略与 BlockingQueue 有关。

    6. threadFactory

    线程工厂,主要用来创建线程,默认为正常优先级、非守护线程。

    7. handler

    拒绝策略,拒绝处理任务时的策略,系统提供了 4 种可选:
    AbortPolicy:拒绝并抛出异常。
    CallerRunsPolicy:使用当前调用的线程来执行此任务。
    DiscardOldestPolicy:抛弃队列头部(最旧)的一个任务,并执行当前任务。
    DiscardPolicy:忽略并抛弃当前任务。
    默认策略为 AbortPolicy。

    二、任务执行流程

    • 当线程数小于核心线程数时,创建核心线程,然后执行任务。当核心线程的worker执行完任务时,会调用workQueue的take方法去队列里阻塞的取任务。
    • 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
    • 当线程数大于等于核心线程数,且任务队列已满:若线程数小于最大线程数,创建线程;若线程数等于最大线程数,抛出异常,拒绝任务。非核心线程执行完任务时,会调用workQueue的poll方法去里取任务,当等待超过了等待时长keepAliveTime,就会返回,然后销毁该worker。

    三、线程池的状态转换

    1. 转换流程图

    线程池状态转换.jpg

    2. 各个状态

    1. RUNNING:

    线程池一旦被创建,就处于 RUNNING 状态,任务数为 0,能够接收新任务,对已排队的任务进行处理。

    2. SHUTDOWN:

    不接收新任务,但能处理已排队的任务。调用线程池的 shutdown() 方法,线程池由 RUNNING 转变为 SHUTDOWN 状态。

    3. STOP:

    不接收新任务,不处理已排队的任务,并且会中断正在处理的任务。调用线程池的 shutdownNow() 方法,线程池由(RUNNING 或 SHUTDOWN ) 转变为 STOP 状态。

    4. TIDYING:

    SHUTDOWN 状态下,任务数为 0, 其他所有任务已终止,线程池会变为 TIDYING 状态,会执行 terminated() 方法。线程池中的 terminated() 方法是空实现,可以重写该方法进行相应的处理。
    线程池在 SHUTDOWN 状态,任务队列为空且执行中任务为空,线程池就会由 SHUTDOWN 转变为 TIDYING 状态。
    线程池在 STOP 状态,线程池中执行中任务为空时,就会由 STOP 转变为 TIDYING 状态。

    5. TERMINATED:

    线程池彻底终止。线程池在 TIDYING 状态执行完 terminated() 方法就会由 TIDYING 转变为 TERMINATED 状态。

    四、线程池原理

    // 有空再写

    参考资料:

    1. 线程池的7种创建方式,强烈推荐你用它...
    2. 线程池包含哪些状态?

    相关文章

      网友评论

          本文标题:线程池

          本文链接:https://www.haomeiwen.com/subject/nvshaltx.html