使用线程池的好处:
1.重用线程池中的线程,避免因为线程的创建和销毁而带来的开销;
2.有效限制线程池的最大并发数,避免大量的线程之间抢占资源而引起的阻塞;
3.能够对线程进行简单管理,提供定时执行以及指定间隔循环执行等功能;
ThreadPoolExecutor是线程池的真正实现,它的构造方法提供了一系列的参数来配置线程池。
ThreadPoolExecutor的构造参数解释:
1.corePoolSize:
核心线程数,默认情况下,核心线程会一直在线程池中存活,即使处于闲置状态;如果将TheadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔由keepAliveTime所指定,当等待时间超过keepAliveTime所指定的时长后,核心线程会被终止。
2.maximumPoolSize:
线程池所能容纳的最大线程数量,当活动线程数超过此数量,后续任务会被阻塞,即等待;
3.keepAliveTime:
非核心线程闲置时的超时时长,超过该时长,非核心线程会被回收;当allowCoreThreadTimeOut属性设置为true时,keepAliveTime同样作用于核心线程;
4.unit:
用于指定keepAliveTime参数的时间单位,是一个枚举,使用TimeUnit.xxx来指定;
5.workQueue:
线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中;
6.thradFactory
线程工厂,为线程池提供创建新线程的功能。
TheadPoolExecutor执行任务的流程:
1.如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务;
2.如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队,等待闲置的核心线程去执行;
3.如果在步骤2中无法将任务插入任务队列,这往往是由于任务队列已经满了,这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行任务;
4.当步骤3中的线程数量已经到达线程池规定的最大值,那么就拒绝执行此任务;ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法来通知调用者;
关于ThreadPoolExecutor的配置,可看AsyncTask的配置,规格如下:
1.核心线程数等于CPU核心数+1;
2.线程池的最大线程数为CPU核心数的2倍+1;
3.核心线程无超时机制,非核心线程在闲置时的超时时间为1s,即非核心线程超过1秒即被回收;
4.任务队列的容量为128;
线程池的分类
这些线程池都是直接或者间接的配置ThreadPoolExecutor得到的;
1.FixedThreadPool
通过Executors.newFixedThreadPool来创建;
它是一种线程数量固定的线程池,这也是Fixed的由来,当线程处于闲置状态时,它们并不会被回收,除非线程池关闭。
当所有的线程都处于活动状态时,新任务会处于等待状态,直到有线程空闲出来,也就是说,当任务过多时,会出现等待执行的情况;
FixedThreadPool只有核心线程并且这些核心线程不会被回收,意味着它可以更快的响应外界的请求;
FixedThreadPool核心线程没有超时机制,并且任务队列也没有大小限制,所以就算线程再多,也不会拒绝执行;
2.CacheThreadPool
通过Executors.newCacheThreadPool来创建;
它是一种线程数量不固定的线程池,他只有非核心线程,并且最大线程数为Integer.MAX_VALUE,即任意大;
当线程池中的线程都处于活动状态,即没有闲置线程时,创建一个新线程来执行处理新任务;不然的话,使用闲置线程来处理,这就是cache的由来;
CacheThreadPool中线程都有一个超时机制,超过60s后闲置线程会被回收;
CacheThreadPool中的任务队列是一个空集合,这将导致任何任务都会被立即执行,因此,它适合执行大量的耗时较少的任务;
当整个线程池都处于闲置状态时,线程池中的线程都会超时而被终止,此时CacheThreadPool中实际上是没有任何线程的,几乎不占任何系统资源;
3.ScheduledThreadPool
通过Executors.newScheduledThreadPool创建,它用于执行定时任务和具有固定周期的重复任务,这也就是Scheduled的由来;
它的核心线程数量是固定,而非核心线程数是没有限制的,并且当非核心线程被闲置时会立即被回收;
4.SingleThreadExecutor
通过Executors.SingleThreadExecutor创建,它内部只有一个核心线程,这就是single的由来,它确保所有的任务都在同一个线程中按顺序执行;
它的任务队列也没有大小限制;
它的意义在于统一所有的外界任务到一个线程中,使得在这些任务之间不需要处理线程同步的问题;
总结:
1.线程池的运作流程:
一个任务要被执行时,如果有闲置的核心线程,就用核心线程去执行;
如果核心线程都在忙碌,那就把任务加入任务队列,等待闲置的核心线程去执行;
如果任务队列的容量也满了,此时如果有非核心线程的话,就去开启非核心线程去执行,非核心线程运行完后,有可能会被回收;
如果开启的线程数量超过线程池最大值,线程池就会拒绝任务;
2.使用场景分析:
如果要执行大量的并发任务,可以使用CacheThreadPool,它有个好处,使用完后就被回收,不怎么占用资源;
如果想定时或者固定周期的执行任务,可以使用ScheduledThreadPool;
如果是顺序执行,不需要处理同步问题,只是新开启个线程去执行任务的话,可以使用SingleThreadPool;
如果想要快速响应外界请求的话,可以使用FixedThreadPool;
其中FixedThreadPool和SingleThreadPool,ScheduledThreadPool可能会需要等待,而CacheThreadPool不需要等待;