✨面试常问✨:如何设置Java线程池大小?

840 阅读5分钟

本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力

Code皮皮虾 一个沙雕而又有趣的憨憨少年,和大多数小伙伴们一样喜欢听歌、游戏,当然除此之外还有写作的兴趣,emm...,日子还很长,让我们一起加油努力叭🌈

欢迎各位小伙伴们关注我的公众号:JavaCodes,名称虽带Java但涉及范围可不止Java领域噢😁,会长期分享博文或者福利,期待您的关注❤

😉毛遂自荐

毛遂自荐,给大家推荐一下自己的专栏😁,欢迎小伙伴们收藏关注😊

MybatisPlus专栏

App爬虫专栏

PC端爬虫专栏

大厂面试题专栏


✨什么是线程池?

线程池: 是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时尽可能重用这些线程而不是新建一个线程。


🌈线程池种类介绍

🌈线程池种类介绍——Executors

image.png


🌝newFixedThreadPool()

newFixedThreadPool():固定线程数的线程池

 public static ExecutorService newFixedThreadPool(int nThreads) {
     return new ThreadPoolExecutor(nThreads, nThreads,
                                   0L, TimeUnit.MILLISECONDS,
                                   new LinkedBlockingQueue<Runnable>());
 }
线程池特点:
  • 核心线程数和最大线程数大小一样
  • 没有所谓的非空闲时间,即keepAliveTime为0
  • 阻塞队列为无界队列LinkedBlockingQueue
缺点
  • 如果某任务执行时间过长,而导致大量任务堆积在阻塞队列中,或者说在某一时刻大量任务进来则会导致机器内存使用不断飙升,最终导致OOM
使用场景

newFixedThreadPool 适用于处理CPU密集型的任务,确保CPU在长期被工作线程使用的情况下,尽可能的少的分配线程,即适用执行长期的任务。


🌑newCachedThreadPool()

 public static ExecutorService newCachedThreadPool() {
     return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                   60L, TimeUnit.SECONDS,
                                   new SynchronousQueue<Runnable>());
 }
线程池特点:
  • 核心线程数为0
  • 最大线程数为Integer.MAX_VALUE
  • 阻塞队列是SynchronousQueue
  • 非核心线程空闲存活时间为60秒
缺点
  • 如果任务的提交速度大于线程处理任务的速度,那么就会不断地创建新线程极端情况下会耗尽CPU和内存资源
  • CachedThreadPool允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。
使用场景

适用于并发执行大量短期的小任务。

SynchronousQueue是无界的,是一种无缓冲的等待队列


🌒newSingleThreadExecutor()

 public static ExecutorService newSingleThreadExecutor() {
     return new FinalizableDelegatedExecutorService
         (new ThreadPoolExecutor(1, 1,
                                 0L, TimeUnit.MILLISECONDS,
                                 new LinkedBlockingQueue<Runnable>()));
 }
线程池特点
  • 核心线程数为1
  • 最大线程数也为1
  • 阻塞队列是LinkedBlockingQueue
  • keepAliveTime为0
缺点

LinkedBlockingQueue 为无界队列,可能会导致OOM

使用场景

适用于串行执行任务的场景,一个任务一个任务地执行。


🌓newScheduledThreadPool()

 public ScheduledThreadPoolExecutor(int corePoolSize) {
     super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
           new DelayedWorkQueue());
 }
线程池特点
  • 最大线程数为Integer.MAX_VALUE
  • 阻塞队列是DelayedWorkQueue
  • keepAliveTime为0
  • scheduleAtFixedRate() :按某种速率周期执行
  • scheduleWithFixedDelay():在某个延迟后执行
使用场景

周期性执行任务的场景,需要限制线程数量的场景


🌈线程池种类介绍——自定义线程池:ThreadPoolExecutor

阿里巴巴Java开发手册禁止使用Executors创建线程池,而是推荐ThreadPoolExecutor来自定义线程池


ThreadPoolExecutor七大参数

corePoolSize 核心线程数,没达到核心线程数时,会创建新的线程。当达到核心线程数时,任务会进去队列

maximumPoolSize 最大线程数,可以为Integer.MAX_VALUE 21亿。当达到核心线程数且队列满了的时候,会去创建额外的线程来执行任务,最多不超过最大线程数

keepAliveTime 存活时间,当任务处理完成,额外的线程存活一段时间后,会自行销毁。空闲等待时间(该参数默认对核心线程无效,当allowCoreThreadTimeOut手动设置为true时,核心线程超过存活时间后才会被销毁)

TimeUnit 空闲等待时间的单位

BlockingQueue :任务进来,如果核心线程数满了,则任务进入队列中等待。

ThreadFactory 线程创建工厂

RejectExecutionHandler 拒绝策略,当最大线程数满了并且队列也满了的时候,如果再有任务进来就会启用拒绝策略。

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}


🔥如何设置Java线程池大小?

公式

  • CPU 密集型任务(N+1): 这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1,比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。 一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。
  • I/O 密集型任务(2N): 这种任务应用起来,系统会用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N。

设置的是最大线程数

如何判断是 CPU 密集任务还是 IO 密集任务?

CPU 密集型简单理解就是利用 CPU 计算能力的任务比如你在内存中对大量数据进行排序。

但凡涉及到网络读取,文件读取这类都是 IO 密集型,这类任务的特点是 CPU 计算耗费时间相比于等待 IO 操作完成的时间来说很少,大部分时间都花在了等待 IO 操作完成上。


❤最后

我是 Code皮皮虾,一个热爱分享知识的 皮皮虾爱好者,未来的日子里会不断更新出对大家有益的博文,期待大家的关注!!!

创作不易,如果这篇博文对各位有帮助,希望各位小伙伴可以==一键三连哦!==,感谢支持,我们下次再见~~~


一键三连.png