线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。这里的线程就是我们前面学过的线程,这里的任务就是我们前面学过的实现了Runnable或Callable接口的实例对象;
我们要想自定义线程池,必须先了解线程池的工作原理,才能自己定义线程池; 这里我们通过观察java中ThreadPoolExecutor的源码来学习线程池的原理; (源码演示在idea中查看)
构造方法:
public ThreadPoolExecutor(
int corePoolSize, //核心线程数量
int maximumPoolSize, //最大线程数
long keepAliveTime, //最大空闲时间
TimeUnit unit, //时间单位
BlockingQueue workQueue, //任务队列
ThreadFactory threadFactory, //线程工厂
RejectedExecutionHandler handler //饱和处理机制
)
{ ... }
(1)核心线程数(corePoolSize)
核心线程数的设计需要依据任务的处理时间和每秒产生的任务数量来确定,例如:执行一个任务需要0.1秒,系统百分之80的时间每秒都会产生100个任务,那么要想在1秒内处理完这100个任务,就需要10个线程,此时我们就可以设计核心线程数为10;当然实际情况不可能这么平均,所以我们一般按照8020原则设计即可,既按照百分之80的情况设计核心线程数,剩下的百分之20可以利用最大线程数处理;
(2)任务队列长度(workQueue)
任务队列长度一般设计为:核心线程数/单个任务执行时间*2即可;例如上面的场景中,核心线程数设计为10,单个任务执行时间为0.1秒,则队列长度可以设计为200;
(3)最大线程数(maximumPoolSize)
最大线程数的设计除了需要参照核心线程数的条件外,还需要参照系统每秒产生的最大任务数决定:例如:上述环境中,如果系统每秒最大产生的任务是1000个,那么,最大线程数=(最大任务数-任务队列长度)*单个任务执行时间;既: 最大线程数=(1000-200)*0.1=80个;
(4)最大空闲时间(keepAliveTime)
这个参数的设计完全参考系统运行环境和硬件压力设定,没有固定的参考值,用户可以根据经验和系统产生任务的时间间隔合理设置一个值即可;
ExecutorService接口是java内置的线程池接口,通过学习接口中的方法,可以快速的掌握java内置线程池的基本使用
void shutdown() 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
List
Future> submit(Runnable task) 执行 Runnable 任务,并返回一个表示该任务的 Future。
思考: 既然ExecutorService是一个接口,接口是无法直接创建对象的,那么我们该如何获取ExecutorService的对象呢?
①每提交一个任务就创建一个线程
static ExecutorService newCachedThreadPool() 创建一个默认的线程池对象,里面的线程可重用,且在第一次使用时才创建
static ExecutorService newCachedThreadPool(ThreadFactorythreadFactory) 线程池中的所有线程都使用ThreadFactory来创建,这样的线程无需手动启动,自动执行;
②可以创建固定数量的线程池
static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池
static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)创建一个可重用固定线程数的线程池且线程池中的所有线程都使用ThreadFactory来创建。
③整个线程池只有一个线程,任务需要排队来进行处理
static ExecutorService newSingleThreadExecutor() 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)创建一个使用单个 worker 线程的 Executor,且线程池中的所有线程都使用ThreadFactory来创建。
ScheduledExecutorService是ExecutorService的子接口,具备了延迟运行或定期执行任务的能力
① static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)创建一个可重用固定线程数的线程池且允许延迟运行或定期执行任务;
② static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)创建一个可重用固定线程数的线程池且线程池中的所有线程都使用ThreadFactory来创建,且允许延迟运行或定期执行任务;
③ static ScheduledExecutorService newSingleThreadScheduledExecutor()
创建一个单线程执行程序,它允许在给定延迟后运行命令或者定期地执行。
④ static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) 创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。
①
延迟时间单位是unit,数量是delay的时间后执行callable。
② ScheduledFuture> schedule(Runnable command, long delay, TimeUnit unit)
延迟时间单位是unit,数量是delay的时间后执行command。
③ ScheduledFuture> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
延迟时间单位是unit,数量是initialDelay的时间后,每间隔period时间重复执行一次command。
④ ScheduledFuture> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。
我们刚刚在学习java内置线程池使用时,没有考虑线程计算的结果,但开发中,我们有时需要利用线程进行一些计算,然后获取这些计算的结果,而java中的Future接口就是专门用于描述异步计算结果的,我们可以通过Future 对象获取线程计算的结果;
Future 的常用方法如下:
① boolean cancel(boolean mayInterruptIfRunning) 试图取消对此任务的执行。
② V get() 如有必要,等待计算完成,然后获取其结果。
③ V get(long timeout, TimeUnit unit) 如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。
④ boolean isCancelled() 如果在任务正常完成前将其取消,则返回 true。
⑤ boolean isDone() 如果任务已完成,则返回 true