【JUC-4】线程池实战应用

本文详细介绍了Java中线程池的创建方式,包括不推荐的Executors工具类和推荐的ThreadPoolExecutor。讨论了各种线程池的特性,如固定大小、缓存、单线程和定时线程池。还解析了ThreadPoolExecutor的关键参数,如corePoolSize、maximumPoolSize、keepAliveTime以及四种拒绝策略。最后提到了线程池的状态转换和关闭方法,以及如何根据任务类型(CPU密集型或IO密集型)合理设置线程数。
摘要由CSDN通过智能技术生成

线程池

线程池创建方式

Executors创建线程池(不推荐)

JDK提供的工具类Execurtors创建线程池(不推荐), 列举几个Executors中创建线程池的方法; 查看Executors的源代码发现, 它创建线程池也是通过 new ThreadPoolExecutor() 来创建线程池的. 当然其中有一些特殊的线程池也不是通过ThreadPoolExecutor来创建的。 比如newWorkStealingPool(int parallelism) 方法就是通过ForkJoinPool来创建线程池的. 也有通过ScheduledThreadPoolExecutor创建调度线程池的方法.

固定大小线程池源码:

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

Executors中方法列表:

方法描述
newFixedThreadPool(int nThreads)创建一个固定大小的线程池,该线程池中的线程数量始终为指定的数量。
newCachedThreadPool()创建一个根据需要自动调整大小的线程池。当有任务提交时,如果有空闲线程,则会重用空闲线程执行任务;该线程池没有缓冲队列
newSingleThreadExecutor()创建一个只包含单个线程的线程池。线程池中只有一个线程,用于顺序执行任务。
newScheduledThreadPool(int corePoolSize)创建一个具有指定核心线程数的定时任务线程池。可以用于执行定时任务和周期性任务
newWorkStealingPool(int parallelism)创建一个具有指定并行度的工作窃取线程池。该线程池基于ForkJoinPool实现,用于执行计算密集型任务。线程池中的线程数量为指定的并行度。

ThreadPoolExecutor创建线程池(推荐)

实际开发中, 也是经常通过ThreadPoolExecutor通过自定义指定核心线程数/最大线程数/缓冲队列/拒绝策略 来创建线程池的.

ThreadPoolExecutor类图

在这里插入图片描述

ThreadPoolExecutor参数说明:

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)


  • corePoolSize:线程池的核心线程数,即线程池中始终保持的活动线程数量,即使它们处于空闲状态。当有新任务提交时,线程池会优先创建核心线程来执行任务。
  • maximumPoolSize:线程池允许的最大线程数,包括核心线程和非核心线程。当工作队列已满且当前线程数小于最大线程数时,线程池会创建新的线程来执行任务。如果设置为无界值(Integer.MAX_VALUE),则最大线程数没有限制。
  • keepAliveTime:非核心线程的空闲时间超过该值时,会被回收(终止)。即当线程池中的线程数量大于核心线程数,并且空闲时间超过keepAliveTime时,多余的线程会被回收,直到线程数量不超过核心线程数。
  • unit:空闲时间的时间单位,例如TimeUnit.SECONDS表示秒。
  • workQueue:用于保存待执行任务的阻塞队列。可以使用不同类型的阻塞队列,如ArrayBlockingQueueLinkedBlockingQueueSynchronousQueue等。选择合适的队列类型可以影响线程池的行为。

​ 除了上述参数之外,ThreadPoolExecutor还提供了其他构造函数和一些可选的参数,例如:

  • ThreadFactory:用于创建新线程的工厂类。可以自定义线程的名称、优先级等属性。

  • RejectedExecutionHandler:当线程池无法接受新任务时,定义了新任务的拒绝策略。常见的拒绝策略包括抛出异常、丢弃任务、丢弃队列中最旧的任务等。

    ThreadPoolExecutor中默认实现了4种拒绝策略

    1. AbortPolicy(默认):当线程池无法接受新任务时,会抛出RejectedExecutionException异常,拒绝新任务的提交。
    2. CallerRunsPolicy:当线程池无法接受新任务时,新任务会由提交任务的线程执行,而不是交给线程池中的线程执行。这样可以降低任务提交的速度,但可能会影响原始任务提交线程的性能。
    3. DiscardPolicy:当线程池无法接受新任务时,会直接丢弃新任务,不做任何处理。新任务被丢弃后,不会给出任何提示或记录。
    4. DiscardOldestPolicy:当线程池无法接受新任务时,会丢弃队列中最旧的任务,然后尝试重新提交新任务。

    也可以通过实现RejectedExecutionHandler接口来自定义拒绝策略.

线程池的状态

  1. 运行(Running):线程池处于正常运行状态,可以接受任务提交,并且正在执行任务。
  2. 关闭(Shutdown):调用了线程池的 shutdown() 方法后,线程池处于关闭状态。线程池不再接受新的任务提交,但会继续执行已提交的任务,直到所有任务完成。
  3. 停止(Stopped):调用了线程池的 shutdownNow() 方法后,线程池处于停止状态。线程池不再接受新的任务提交,并且尝试中断正在执行的任务。已提交但尚未开始执行的任务可能会被丢弃。
  4. 终止(Terminated):当线程池处于关闭状态且所有任务都已完成时,线程池进入终止状态。此时,线程池中的所有线程都已停止。

ThreadPoolExecutor方法列表

方法说明
Future submit(Callable c)向线程池提交任务,由线程池调度线程执行任务
List<Future> invokeAll(Collection<? extends Callable> tasks)invokeAll传入一个任务集合,由线程池调度执行集合中全部任务; 调用该方法,会阻塞主线程. 直到线程池中任务运行
T invokeAny(Collection<? extends Callable> tasks)T invokeAny(Collection<? extends Callable> tasks)
void shutdown()改变线程池状态为shutdown, 不再接受新的任务提交, 会将缓冲队列中的任务执行完
List shutdownNow()不会接受新的任务, 将队列中的任务返回, 并且使用interrupt方法打断正在执行的线程

线程池设置多少个线程数比较合适

CPU密集型:

CPU核数 + 1能够实现CPU最佳利用率; +1 是保证当操作系统页缺失故障时, 或其他原因导致暂停时, 额外的线程就能工作, 保证CPU时钟周期不被浪费.

IO密集型:

线程数 = 核数 * 期望CPU利用率 * 总时间(CPU计算时间+等待时间) / CPU计算时间

例如 2核CPU计算时间是 20%, 它的等待时间就是 80% , 期望CPU利用率是90%, 套用公式:

2 * 90% * 100% / 20% = 9

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

干饭两斤半

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值