问十二:说说你了解到的java线程池?

1.线程池优点

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

第三:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

 

2. 源码分析

        1)线程池的创建

在创建线程池时会传入7个参数,分别为:

corePoolSize:线程池核心线程数量

核心线程会一直存活,即使没有任务需要执行。当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理

设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭

如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程

 

maximumPoolSize:线程池最大线程数量

当线程数>=corePoolSize,且任务队列已满时,线程池会创建新线程来处理任务

当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常

 

keepAliverTime:非核心线程存活时间

活跃线程数大于核心线程数时,空闲的多余线程最大存活时间,当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize

如果allowCoreThreadTimeout=true,则会直到线程数量=0

 

unit:存活时间的单位

 

workQueue:存放任务的队列

用来存储等待执行的任务,决定了线程池的排队策略,可以选择以下几个阻塞队列:

ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原则对元素进行排序【有界队列】

LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue【无界队列】

SynchronousQueue:不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 LinkedBlockingQueue【无界队列】

PriorityBlockingQueue:一个具有优先级的无界阻塞队列【无界队列】

 

threadFactory:创建新线程使用的工厂

 

handler:超出线程范围和队列容量的任务的处理程序(线程池的饱和策略)

RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。在JDK 1.5中Java线程池框架提供了以下4种策略:

AbortPolicy:直接抛出异常

CallerRunsPolicy:使用调用者所在线程来运行任务

DiscardPolicy:不处理,丢弃掉

DiscardOldestPolicy:抛弃下一个即将被执行的任务,然后尝试重新提交新的任务(注:此策略一般不和PriorityBlockingQueue一起使用,因为会导致优先级最高的任务被丢弃)

2)向线程池提交任务(线程池的增长策略)

 

可以使用两个方法向线程池提交任务,分别为execute()和submit()方法

execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功

submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。

 

execute()方法逻辑为:(就是线程池的原理)

1.如果线程池中的线程数量少于corePoolSize(核心线程数量),那么会直接开启一个新的核心线程来执行任务,即使此时有空闲线程存在.

2.如果线程池中线程数量大于等于corePoolSize(核心线程数量),那么任务会被插入到任务队列中排队等待被执行.此时并不添加新的线程.

3.如果在步骤2中由于任务队列已满导致无法将新任务进行排队,这个时候有两种情况:

线程数量未达到maximumPoolSize(线程池最大线程数) , 立刻启动一个非核心线程来执行任务.

线程数量已达到maximumPoolSize(线程池最大线程数) , 拒绝执行此任务.ThreadPoolExecutor会通过RejectedExecutionHandler,抛出RejectExecutionException异常。

3)关闭线程池

可以通过调用线程池的shutdown或shutdownNow方法来关闭线程池。

它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止

shutdownNow首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表

shutdown只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程

3. 合理配置线程池

获取CPU个数:Runtime.getRuntime().availableProcessors()

CPU密集型:CPU负载高,IO读取效率很高

           配置 (CPU核数+1 个线程

IO密集型:CUP负载低,IO读取耗时高

            配置 CPU核数*(1 + 平均等待时间/平均工作时间) 个线程

混合型的任务,如果可以拆分,将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐量将高于串行执行的吞吐量。如果这两个任务执行时间相差太大,则没必要进行分解。

 

全部评论

相关推荐

某物流公司 软件开发岗 总包26-30
点赞 评论 收藏
转发
点赞 收藏 评论
分享
牛客网
牛客企业服务