Java中通过Executors调用静态方法来提供四种线程池介绍

本文详细介绍了Java中四种常见的线程池实现:可缓存线程池(newCachedThreadPool)、定时线程池(newScheduledThreadPool)、固定大小线程池(newFixedThreadPool)以及单线程化线程池(newSingleThreadExecutor)。同时讲解了线程池的生命周期管理和任务队列及拒绝策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java中的线程池用ThreadPoolExecutor类来表示,ThreadPoolExecutor这个类继承自抽象类AbstractExecutorService,AbstractExecutorService又实现了ExecutorService接口,ExecutorService接口又继承了Executor接口。

ThreadPoolExecutor类详细说明在本博客中其他篇幅有介绍,如果需要请参考

07170436_LBnb.jpg

从参数便可以看到几个构造方式的不同,但是若跟源码之后,会发现,前面三个都调用了第四个。

跟源码之后会发现,其实这些静态方法里面也是调用了ThreadPoolExecutor的构造方法,例如:

07170436_sd2i.jpg

只不过Executors帮我们配置了一些参数;下面介绍下Executors方式:

1、newCachedThreadPool 方法,它创建了一个可缓存的线程池,如果线程池的长度超过处理需要,它可灵活回收空闲线程,若无可回收,则新建线程。

2newScheduledThreadPool 方法,它创建了一个定长线程池,支持定时及周期性的任务执行。

3、newFixedThreadPool 方法,它创建了一个定长线程池,可以控制线程最大并发数,超出的线程会在队列中等待。

4、newSingleThreadExecutor 方法,它创建了一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有的任务按照指定的顺序(FIFO, LIFO, 优先级)来执行的。

 

一、newCachedThreadPool(可以缓存的线程池

首先,我们一起来创建一个可以缓存的线程池,如果线程池的长度超过了处理需要的话,它可灵活的回收空闲的线程,若无可回收的线程,则就新建线程。代码如图1所示:

110225_7KEI_1054538.png

当前线程:pool-1-thread-1输出:0
当前线程:pool-1-thread-1输出:1
当前线程:pool-1-thread-1输出:2
当前线程:pool-1-thread-1输出:3
当前线程:pool-1-thread-1输出:4
当前线程:pool-1-thread-1输出:5
当前线程:pool-1-thread-1输出:6
当前线程:pool-1-thread-1输出:7
当前线程:pool-1-thread-1输出:8
当前线程:pool-1-thread-1输出:9

 

以上第一种方式创建线程池,线程池为无限大的,当其执行到第二个任务的时候,第一个任务已经完成了,并且会复用执行第一个任务的线程,进而不用每次新建线程。

改变下,去掉睡眠,注释掉,输出结果

//Thread.sleep(i*1000);

当前线程:pool-1-thread-1输出:0
当前线程:pool-1-thread-3输出:2
当前线程:pool-1-thread-3输出:9
当前线程:pool-1-thread-5输出:4
当前线程:pool-1-thread-7输出:6
当前线程:pool-1-thread-2输出:1
当前线程:pool-1-thread-9输出:8
当前线程:pool-1-thread-6输出:5
当前线程:pool-1-thread-8输出:7
当前线程:pool-1-thread-4输出:3
去掉休眠,这种方式将会创建1-10个线程,获取cup时间片执行

二、newScheduledThreadPool(定长的线程池)

然后,我们一起来创建一个定长的线程池,这个线程池它支持定时及周期性的任务执行。延迟执行的示例代码如下图所示:表示延迟3秒执行代码。

133400_csDa_1054538.png

当前线程:pool-1-thread-1输出:0
当前线程:pool-1-thread-1输出:2
当前线程:pool-1-thread-3输出:1
当前线程:pool-1-thread-3输出:4
当前线程:pool-1-thread-1输出:3
当前线程:pool-1-thread-2输出:5
当前线程:pool-1-thread-2输出:6
当前线程:pool-1-thread-2输出:7
当前线程:pool-1-thread-2输出:9
当前线程:pool-1-thread-4输出:8

如下图:表示延迟1秒后,每隔5秒执行一次代码即每隔5秒重复执行一次

133819_Q8aS_1054538.png

当前线程:pool-1-thread-4输出:3
当前线程:pool-1-thread-1输出:0
当前线程:pool-1-thread-5输出:4
当前线程:pool-1-thread-2输出:1
当前线程:pool-1-thread-2输出:8
当前线程:pool-1-thread-2输出:9
当前线程:pool-1-thread-3输出:2
当前线程:pool-1-thread-5输出:7
当前线程:pool-1-thread-1输出:6
当前线程:pool-1-thread-4输出:5
当前线程:pool-1-thread-2输出:0
当前线程:pool-1-thread-1输出:3
当前线程:pool-1-thread-4输出:4
当前线程:pool-1-thread-5输出:2
当前线程:pool-1-thread-2输出:6
当前线程:pool-1-thread-2输出:8
当前线程:pool-1-thread-2输出:9
当前线程:pool-1-thread-3输出:1
当前线程:pool-1-thread-5输出:7
当前线程:pool-1-thread-4输出:5

三、newFixedThreadPool(定长的线程池

然后,我们一起来创建一个定长的线程池,可以控制线程的最大并发数,超出的任务会在队列中等待。示例代码如下所示:

134748_0Dx2_1054538.png

以上第三种方式创建线程池,因为线程池大小为4,每个任务输出index后sleep 3秒,所以每3秒打印4个数字。

定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()

四、newSingleThreadExecutor

最后,我们一起来创建一个单线程化的线程池,单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)来执行。示例代码如图:

142237_8Svj_1054538.png

当前线程:pool-1-thread-1输出:0
当前线程:pool-1-thread-1输出:1
当前线程:pool-1-thread-1输出:2
当前线程:pool-1-thread-1输出:3
当前线程:pool-1-thread-1输出:4
当前线程:pool-1-thread-1输出:5
当前线程:pool-1-thread-1输出:6
当前线程:pool-1-thread-1输出:7
当前线程:pool-1-thread-1输出:8
当前线程:pool-1-thread-1输出:9

代码的结果是依次输出的,就相当于是按顺序的执行了各个任务。

五.线程池也是有生命周期的

07170436_u0Rx.jpg

runState表示当前线程池的状态。

当线程池初始创建的时候,是running态。

当线程池处于shutdown态的时候,此线程池不能够接受新的任务,它会等待所有的任务执行完毕。

当线程池处于stop状态的时候,此线程不能接受新的任务,并且会去尝试终止正在执行的任务。

那线程池什么时候处于什么状态呢。当调用了shutdown()方法的时候就会处于shutdown状态,当调用了shutdownNow()方法的时候就会处于stop状态。

六.任务队列及拒绝措施

 

线程池中的线程是有数量限制的,根据任务类型来设置线程数量,如果是CPU密集型,则数量为NCPU+1,如果是IO密集型,则数量为2*NCPU,很多时候,为了安全性,还是会设置上限,当新的任务来的时候,如果正常数量的线程都在用着,那么就用一个队列数据结构把新的任务缓存起来,若是新任务增长速度大于线程完成任务的速度,这时可以在线程数量还没有达到上限的时候创建新的线程来执行任务,但若是达到了上限而且任务缓存队列已经满了的话,则有必要采取拒绝措施,拒绝后续任务的到来。

这里有两个概念:

1)任务缓存队列(详细介绍在本博客其他篇幅有详细介绍,如有需要请参考

07170436_HAYQ.jpg

常有这四种情况,我们选用一个东西的时候肯定是根据它的某些特征。

A、ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小。

B、PriorityBlockingQueue:基于优先级的队列。

C、LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定大小,则默认为Integer.MAX_VALUE。

D、synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新的任务。

2)任务的拒绝措施:如果线程池中任务缓存队列已经满了并且线程池中线程数目达到maxmunPoolSize,如果还有任务来,就会采取任务拒绝策略,通常有一些四种策略。

A、ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。

B、ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。

C、ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)

D、ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

附录:返回ExecutorService接口

164319_Sbro_1054538.png

164257_SQKV_1054538.png

ExecutorService接口方法

162139_4NPs_1054538.png

ExecutorService实现类

164437_SriF_1054538.png

164554_IAav_1054538.png

164748_S4pG_1054538.png

顶层Executor接口的,最终在ThreadPoolExecutor里面有了具体的实现,这个方法就是用来让线程池执行某个任务的。

164927_MDu8_1054538.png

转载于:https://my.oschina.net/u/1054538/blog/1619619

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值