线程池参数设置
hello丫,各位小伙伴们,好久不见了!
下面,我们先来复习一下线程池的参数
1、线程池参数有哪些?
-
corePoolSize(核心线程数)
:线程池中的常驻核心线程数。即使这些线程处于空闲状态,它们也不会被销毁。除非设置了allowCoreThreadTimeOut,否则核心线程在空闲时也不会被终止。 -
maximumPoolSize(最大线程数)
:线程池中允许的最大线程数。当工作队列已满,并且正在执行的线程数等于corePoolSize时,线程池会创建新的线程来处理任务,直到线程数达到maximumPoolSize。 -
keepAliveTime(线程空闲时间)
:当线程数大于corePoolSize时,这是多余空闲线程等待新任务的最长时间。超过这个时间后,多余的空闲线程将被终止。这个参数帮助减少系统资源消耗。 -
unit(时间单位)
:keepAliveTime参数的时间单位,例如SECONDS、MILLISECONDS等。 -
workQueue(任务队列)
:用于保存等待执行的任务的阻塞队列。当所有核心线程都在工作,新提交的任务会被添加到这个队列中等待执行。队列的类型(例如ArrayBlockingQueue、LinkedBlockingQueue或SynchronousQueue)会影响线程池的行为。 -
threadFactory(线程工厂)
:用于创建新线程的工厂,它提供了定制新线程的方式,例如设置线程名、是否为守护线程等。 -
handler(拒绝策略)
:当线程池无法处理新任务时(例如,任务队列已满,且线程数已达到maximumPoolSize),这个策略定义了应该如何处理。Java提供了几种内置的拒绝策略,如AbortPolicy(抛出异常)、CallerRunsPolicy(调用者运行任务)、DiscardOldestPolicy(丢弃队列中最老的任务)和DiscardPolicy(直接丢弃任务)。也可以根据需要实现自定义的拒绝策略。
2、线程池核心线程数和最大线程数怎么设置?
-
分为
CPU密集型
和IO密集型
-
CPU这种任务消耗的主要是 CPU 资源,可以将线程数设置为
N(CPU 核心数)+1
,比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。 -
IO密集型 这种任务应用起来,系统会用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占 用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们 可以多配置一些线程,具体的计算方法可以是 :
核心线程数=CPU核心数量*2
。 -
最大线程数 一般设置为核心线程数的2~3倍,这个数值是不固定的
实际工程经验可能并不是严格按照这个来的,我们要根据实际开发场景来设置。
3、如何获取CPU核数
Runtime.getRuntime().availableProcessors();
4、线程池工作流程(原理)
通俗易讲来说就是:
内部是通过队列+线程实现的,当我们利用线程池执行任务时,如果此时线程池中的线程数量小于corePoolSize(核心线程数),即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务,如果此时线程池中的线程数量等于corePoolSize,但是阻塞队列(LinkedBlockingQueue)未满,那么任务被放入阻塞队列。如果此时线程池中的线程数量大于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolize,建新的线程来处理被添加的任务。如果此时线程池中的线程数量大于corePolSize,缓冲队列workQueue满。并且线程池中的数量等于maximumPoolize,那么通过 handler所指定的策略来处理此任务。当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数
5、那么再来复习一下线程池的4中拒绝策略
- AbortPolicy。这是线程池的默认策略。当任务被拒绝时,它会抛出`RejectedExecutionException`异常。这种方式直接告知任务被拒绝,开发者可以根据业务逻辑决定是否重试或放弃提交新任务。
- DiscardPolicy。在这种策略下,当新任务被提交时,如果线程池无法处理,将被直接丢弃,不会有任何通知。这种策略适用于对任务执行不敏感的场景,因为它可能会导致数据丢失。
- DiscardOldestPolicy。如果线程池未关闭且无法执行新任务,则会丢弃任务队列中的最老任务(即存活时间最长的任务),然后可能尝试重新提交被拒绝的任务。这种策略旨在腾出空间给新提交的任务,但同样存在数据丢失的风险。
- CallerRunsPolicy。当有新任务提交且线程池无法执行时,该策略会让提交任务的线程直接执行该任务。这提供了最完善的处理方式,因为它避免了任务丢失,但可能会因为任务提交速度过快而导致程序阻塞。
6、如何合理设置线程池的大小:
想要合理配置线程池线程数的大小,需要分析任务的类型,任务类型不同,线程池大小配置也不同。
配置线程池的大小可根据以下几个维度进行分析来配置合理的线程数:
任务性质可分为:CPU密集型任务,IO密集型任务,混合型任务。
任务的执行时长。
任务是否有依赖——依赖其他系统资源,如数据库连接等。
CPU密集型任务
尽量使用较小的线程池,一般为CPU核心数+1。
因为CPU密集型任务使得CPU使用率很高,若开过多的线程数,只能增加上下文切换的次数,因此会带来额外的开销。
IO密集型任务
可以使用稍大的线程池,一般为2*CPU核心数+1。
因为IO操作不占用CPU,不要让CPU闲下来,应加大线程数量,因此可以让CPU在等待IO的时候去处理别的任务,充分利用CPU时间。
混合型任务
可以将任务分成IO密集型和CPU密集型任务,然后分别用不同的线程池去处理。
只要分完之后两个任务的执行时间相差不大,那么就会比串行执行来的高效。
因为如果划分之后两个任务执行时间相差甚远,那么先执行完的任务就要等后执行完的任务,最终的时间仍然取决于后执行完的任务,而且还要加上任务拆分与合并的开销,得不偿失
依赖其他资源
如某个任务依赖数据库的连接返回的结果,这时候等待的时间越长,则CPU空闲的时间越长,那么线程数量应设置得越大,才能更好的利用CPU。
到这里就结束,感谢大家的支持!!!我会继续努力的