这篇主要说一下Executor
框架及线程池。
什么是线程池
顾名思义,线程池就是存放线程的池子,池子里面存放的是已经创建好的N个线程;Java
里面一般用List
或Set
等容器类来存储线程,实现线程池功能。
为什么要使用线程池
首先,线程的创建和销毁是很耗费时间和资源的一件事情。
其次,线程不能无限制的创建,每个线程都会占用内存资源,而且如果线程过多,线程之间的调度也是一件很消耗系统性能的事情。
线程池带来的好处
可以做到随用随取,节省因创建线程而花费的时间
通过设置线程池容量,可以保证创建的线程数量在一个合理范围区间,不会耗光系统资源。
如何创建线程池
Java
提供了Executor
框架,可以让我们简单方便的使用线程池。
//创建一个为容量为2的线程池
Executor exec = Executors.newFixedThreadPool(2);
//向线程池提交5个任务
for (int i = 0; i < 5; i++){
exec.execute(new Task());
}
Executors
Executors是Executor
框架提供的一个工具类,该工具类提供了一些方法,让我们可以方便的创建各种类型的线程池。比如
newCachedThreadPool()
: 创建具有缓存功能的线程池,如果线程数量超过处理需求时,可以对空闲线程进行回收,当线程数量不够时,则新增线程。
newFixedThreadPool()
:创建固定容量的线程池,一旦创建数量设置好就不会改变。
newScheduledThreadPool()
: 创建可以延迟或者定时调度的固定容量的线程池。
newSingleThreadExecutor()
:创建只有一个线程的线程池。
以上方法都会返回一个 ExecutorService 接口描述的对象。
Executor
Executor
是个接口,只有短短的三行代码。
public interface Executor {
void execute(Runnable command);
}
Executor
接口虽然很简单,但可以将任务的提交和执行成功解耦。
对于我们之前的代码 都是通过 new Thread(runnable).start()
方式驱动任务执行,有了Executor
之后,建议大家都换成Executor
方式驱动任务,见上面的例子。
ExecutorService
Executor
也是有生命周期的。分为运行,关闭和已终止三种状态。运行态表示可以向线程池提交任务;关闭状态表示不可以提交任务,但是已提交的会被执行;已终止状态表示所有任务都已经执行完毕。
为了能够管理Executor
的生命周期,JDK
提供了ExecutorService
接口,该接口继承自Executor
,除了提供跟生命周期相关的shutdown()
等方法外,还提供了一些功能更强大的任务提交方法。
ExecutorService
接口的主要方法如下:
public interface ExecutorService extends Executor {
//平缓关闭,不在接受新任务,但是会等待所有已提交的任务完成
void shutdown();
//强制关闭,尝试关闭所有正在执行的任务,并且不会执行等待中的任务,同时返回那些等待执行的任务
List<Runnable> shutdownNow();
//执行所有任务,并拿到所有任务的返回结果
<T> List<Future<T>> invokeAll(Collec