Java中的线程池类有两个,分别是:ThreadPoolExecutor和ScheduledThreadPoolExecutor,这两个类都继承自ExecutorService。利用这两个类,可以创建各种不同的Java线程池,为了方便我们创建线程池,Java API提供了Executors工厂类来帮助我们创建各种各样的线程池。下面我们分别介绍一下这三个类。
Java线程池ExecutorService继承树:
一、ThreadPoolExecutor
ThreadPoolExecutor是ExecutorService的一个实现类,也是java中最常用的线程池类。ThreadPoolExecutor内部维持了一个线程池,可以执行给定的任务,下面是关于它的具体使用方法。
1.1 ThreadPoolExecutor构造方法及其作用
ThreadPoolExecutor源码中的构造方法:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
1.2 线程数量控制
ThreadPoolExecutor线程池中的线程数量是可变的,其变化范围取决于下面两个变量:
- 1
- 2
- 3
具体线程的分配方式是,当一个任务被添加到线程池:
- 1
- 2
- 3
- 4
- 5
- 6
这样,线程池可以动态的调整池中的线程数。除了corePoolSize
、maximumPoolSize
两个变量外,ThreadPoolExecutor构造方法还有几个参数:
- 1
- 2
- 3
- 4
- 5
1.2 unit
unit 可选的参数为java.util.concurrent.TimeUnit
中的几个静态属性:
- 1
- 2
- 3
- 4
- 5
1.3 workQueue
workQueue是一个BlockingQueue,默认是LinkedBlockingQueue<Runnable>
1.4 handler
handler 是线程池拒绝处理任务的方式,主要有四种类型:
- ThreadPoolExecutor.AbortPolicy()(系统默认):抛出java.util.concurrent.RejectedExecutionException异常
- ThreadPoolExecutor.CallerRunsPolicy():当抛出RejectedExecutionException异常时,会调用rejectedExecution方法
- ThreadPoolExecutor.DiscardOldestPolicy():抛弃旧的任务
- ThreadPoolExecutor.DiscardPolicy():抛弃当前的任务
1.5 创建一个ThreadPoolExecutor
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
二、ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor是ExecutorService的另一个实现类,从上面Java线程池ExecutorService继承树这幅图可以看出,ScheduledThreadPoolExecutor直接继承自ScheduledExecutorService,ScheduledThreadPoolExecutor 类的功能也主要体现在ScheduledExecutorService 接口上,而所以在介绍ScheduledThreadPoolExecutor之前先介绍一下ScheduledExecutorService接口。
2.1 ScheduledExecutorService接口介绍
java.util.concurrent.ScheduledExecutorService
接口继承了ExecutorService,它的最主要的功能就是可以对其中的任务进行调度,比如延迟执行、定时执行等等。
ScheduledExecutorService接口定义:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
从上面接口定义我们知道,提供了四个方法,下面我们就分别介绍:
- 1
- 2
- 3
- 4
- 5
2.1.1 schedule (Runnable task, long delay, TimeUnit timeunit)
这个方法的意思是在指定延迟之后运行task。这个方法有个问题,就是没有办法获知task的执行结果。如果我们想获得task的执行结果,我们可以传入一个Callable的实例(后面会介绍)。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
2.1.2 schedule (Callable task, long delay, TimeUnit timeunit)
这个方法与schedule (Runnable task)
类似,也是在指定延迟之后运行task,不过它接收的是一个Callable实例,此方法会返回一个ScheduleFuture对象,通过ScheduleFuture我们可以取消一个未执行的task,也可以获得这个task的执行结果。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
2.1.3 scheduleAtFixedRate (Runnable, long initialDelay, long period, TimeUnit timeunit)
这个方法的作用是周期性的调度task执行。task第一次执行的延迟根据initialDelay
参数确定,以后每一次执行都间隔period
时长。
如果task的执行时间大于定义的period,那么下一个线程将在当前线程完成之后再执行。整个调度保证不会出现一个以上任务同时执行。
2.1.4 scheduleWithFixedDelay (Runnable, long initialDelay, long period, TimeUnit timeunit)
scheduleWithFixedDelay的参数和scheduleAtFixedRate参数完全一致,它们的不同之处在于对period调度周期的解释。
在scheduleAtFixedRate中,period指的两个任务开始执行的时间间隔,也就是当前任务的开始执行时间和下个任务的开始执行时间之间的间隔。
而在scheduleWithFixedDelay中,period指的当前任务的结束执行时间到下个任务的开始执行时间。
2.1.5 ScheduledExecutorService的关闭
和ExecutorService类似, 我们在使用完ScheduledExecutorService时需要关闭它。如果不关闭的话,JVM会一直运行直,即使所有线程已经关闭了。
关闭ScheduledExecutorService可以使用其继承自ExecutorService接口的shutdown()
和shutdownNow()
方法,两者的区别请参考【Java线程池 ExecutorService】。
2.2 ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,构造参数很简单,只有3个:
- 1
- 2
- 3
- 4
具体使用方法请参考ThreadPoolExecutor或者使用Executors。
三、Executors
创建一个什么样的ExecutorService的实例(即线程池)需要我们的具体应用场景而定,不过Java给我们提供了一个Executors工厂类,它可以帮助我们很方便的创建各种类型ExecutorService线程池,Executors一共可以创建下面这四类线程池:
- 1
- 2
- 3
- 4
- 5
备注:Executors只是一个工厂类,它所有的方法返回的都是ThreadPoolExecutor、ScheduledThreadPoolExecutor这两个类的实例。