Java线程池关键参数及线程池类型选择

一次读懂Java线程池关键参数及线程池类型选择

一、java线程池关键参数

针对java语言,通过线程池源码来分析线程池,线程池源码,可以看到,线程池主要包括以下几个参数:

Java线程池关键参数及线程池类型选择_第1张图片

源码中对每个参数的解释如下:

Java线程池关键参数及线程池类型选择_第2张图片

针对每一个参数,意思就是说:

(1)corePoolSize:核心线程数,该参数说明了在线程池中保持核心线程的数量,由自己定义,刚创建线程池时,里面的核心线程数为0,随着任务的添加,达到核心线程数,执行完任务之后,线程池里面的核心线程数一直维持在设置的核心线程数。

如下所示:开始创建了一个核心线程数为2的线程池,我们打印一下线程池信息,可以看到,线程池里面的核心线程数为0.

Java线程池关键参数及线程池类型选择_第3张图片

接下来,我们向线程池里添加4个任务,注意我们设定的核心线程数为2,当所有任务都执行完之后,我们看线程池里面的线程数有多少。通过下图我们可以看到,线程池里面提交的4个任务已经完成,这时,线程池里面存活的线程数为2(核心线程数),并没有变为0。核心线程数用一句话概括:核心线程数由我们程序员指定,在启动线程池,并没有往里面添加任务时,池子里面的线程数量为0,当达到核心线程数时,无论是否向池子里提不提交任务,池子里至少会保持核心线程数数量。

Java线程池关键参数及线程池类型选择_第4张图片

(2)最大线程数(maximumPoolSize):该参数定义了一个线程池中最多能容纳多少个线程。当一个任务提交到线程池中时,如果线程数量达到了核心线程数,并且任务队列已满,不能再向任务队列中添加任务时,这时会检查任务是否达到了最大线程数,如果未达到,则创建新线程,执行任务,否则,执行拒绝策略。可以通过源码来看一下。如下:可以看出,当调用submit(Runnable task)方法,将任务提交到线程池中时,会调用execute()方法去执行任务,在该方法内,会进行核心线程数,任务队列的判断,最后决定是执行或者是拒绝。总结起来就是:最大线程数参数,是在已经达到核心线程池参数,并且任务队列已经满的情况下,才去判断该参数。

Java线程池关键参数及线程池类型选择_第5张图片

Java线程池关键参数及线程池类型选择_第6张图片

(3)keepAliveTime:(最大线程数—核心线程数)线程存活时间,线程池中大于核心线程数的那部分线程,即线程数总数量减去核心线程数的那部分线程,在执行完任务之后,在线程池中存活的时间。

(4)workQueue:任务队列,用来做缓冲作用,当提交到线程池中的任务核心线程数量不够用时,所有的核心线程都在执行任务,会将任务存到任务队列中,等待核心线程来执行。该参数主要是在核心线程数都在执行任务时,才起作用的参数,主要用来缓存任务。总结一句话:该参数主要是在核心线程数都在执行任务时,用来缓存任务的队列。等待线程去执行。

以上就是java线程池主要参数,下面来介绍一下java中的几种线程池

二、java线程池类型及选择

通过查看源码,java中存在以下线程池:

1.public static ExecutorService newFixedThreadPool()

2.public static ExecutorService newWorkStealingPool()

3.public static ExecutorService newSingleThreadExecutor()

4.public static ExecutorService newCachedThreadPool()

5.newScheduledThreadPool()

如下图所示:

Java线程池关键参数及线程池类型选择_第7张图片

下面分别解释每一种线程池特点和使用场景:

1.public static ExecutorService newFixedThreadPool()

     创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大
多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,
则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何
线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之
前,池中的线程将一直存在。

2.public static ExecutorService newWorkStealingPool()

        jdk1.8新引进的线程池,创建一个拥有多个任务队列(以便减少连接数)的线程池。由于能够合理的使用CPU进行对任务操作(并行操作),所以适合使用在很耗时的任务中。

3.public static ExecutorService newSingleThreadExecutor()

返回一个只有一个线程的线程池,这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去!

4.public static ExecutorService newCachedThreadPool()

          创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行
很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造
的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并
从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资
源。

5.newScheduledThreadPool()

    创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。

各种线程池应用场景

1.CachedThreadPool:适合使用在任务量大但耗时少的任务。

2.FixedThreadPool:适合使用在任务量比较固定但耗时长的任务。

3.ScheduledThreadPool:适合使用在执行定时任务和具体固定周期的重复任务。

4.SingleThreadPool:适合使用在多个任务顺序执行的场景。

5.newWorkStealingPool:适合使用在很耗时的任务中

关于线程的其他问题:

一般任务设多少个线程的问题?

这个需要根据具体任务和机器性能来综合考虑,通过不断的性能测试,分析出最佳线程数量。一般来讲:

1.CPU密集型:cpu利用率较高,设置线程数量和cpu核心数一样即可。使cpu得到充分利用。

2.IO密集型:IO密集型,主要进行长时间的IO操作,cpu利用率不如cpu密集型高,一般设置线程数为CPU两倍。

你可能感兴趣的:(java基础)