1 为什么需要线程池?
- 1.在java中,使用线程来执行异步任务时,线程的创建和销毁需要一定的开销。如果我们为每一个任务创建一个新的线程来执行的话,那么这些线程的创建与销毁将消耗大量的计算资源。
- 2.为每一个任务创建一个新线程来执行,这样的方式可能会使处于高负荷状态的应用最终崩溃。
- 曙光我们将在线程池中创建若干条线程,当有任务需要执行时就从该线程池中获取一条线程来执行任务,如果一时间任务过多,超出线程池的线程数量,那么后面的线程任务就进入一个等待队列进行等待,直到线程池有线程处于空闲时才从等待队列获取要执行的任务进行处理,以此循环.....这样就大大减少了线程创建和销毁的开销,也会缓解我们的应用处于超负荷时的情况。
2 Executor框架的两级调度模型
- 在java线程启动时会创建一个本地操作系统线程,当该java线程终止时,这个操作系统线程也会被回收。
- 每一个java线程都会被一对一映射为本地操作系统的线程,操作系统会调度所有的线程并将它们分别给可用的CPU。
- 所谓的映射方式是这样实现的,在上层,java多线程程序通过把应用分为若干个任务,然后使用用户级的调度器(Executor框架)将这些任务映射为固定数量的线程;在底层,操作系统内核将这些线程映射到硬件处理器上。
从图中我们可以看出,应用程序通过Executor框架控制上层的调度,而下层的调度由操作系统内核控制,下层的调度不受应用程序的控制。
3 Executor框架的结构
Executor框架的结构主要包括3个部分
- 1.任务:包括被执行任务需要实现的接口:Runnable接口或Callable接口
- 2.任务的执行:包括任务执行机制的核心接口Executor,以及继承自Executor的EexcutorService接口。Exrcutor有两个关键类实现了ExecutorService接口(ThreadPoolExecutor和ScheduledThreadPoolExecutor)。
- 3.异步计算的结果:包括接口Future和实现Future接口的FutureTask类
4 类间的关系UML
- Extecutor是一个接口,它是Executor框架的基础,它将任务的提交与任务的执行分离开来。
- ThreadPoolExecutor是线程池的核心实现类,用来执行被提交的任务。
- ScheduledThreadPoolExecutor是一个实现类,可以在给定的延迟后运行命令,或者定期执行命令。ScheduledThreadPoolExecutor比Timer更灵活,功能更强大。
- Future接口和实现Future接口的FutureTask类,代表异步计算的结果。
- Runnable接口和Callable接口的实现类,都可以被ThreadPoolExecutor或者ScheduledThreadPoolExecutor执行。区别就是Runnable无法返回执行结果,而Callable可以返回执行结果。
5 执行关系
分析说明
- 创建任务对象
主线程首先创建实现Runnable或Callable接口的任务对象,工具类Executors可以把一个Runnable对象封装为一个Callable对象,使用如下两种方式:
Executors.callable(Runnable task)或者Executors.callable(Runnable task,Object resule)。
- 执行任务
把Runnable对象直接提交给ExecutorService执行,方法为ExecutorService.execute(Runnable command);
把Runnable对象或者Callable对象提交给ExecutorService执行,方法为ExecutorService.submit(Runnable task)或ExecutorService.submit(Callable task)。
- 注意的是如果执行ExecutorService.submit(...),ExecutorService将返回一个实现Future接口的对象(其实就是FutureTask)。
当然由于FutureTask实现了Runnable接口,我们也可以直接创建FutureTask,然后提交给ExecutorService执行。
6 ExecutorService任务周期管理接口
- Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。
- 真正的线程池接口是ExecutorService。
- Executors类,提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。
- Executor的实现通常都会创建线程来执行任务,但是使用异步方式来执行任务时,由于之前提交任务的状态不是立即可见的,所以如果要关闭应用程序时,就需要将受影响的任务状态反馈给应用程序。
- 为了解决执行服务的生命周期问题,Executor扩展了EecutorService接口,添加了一些用于生命周期管理的方法。如下:
public interface ExecutorService extends Executor {
void shutdown();
List shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
// 省略部分方法
}
参考
https://juejin.im/entry/57ad83932e958a005442862c
https://juejin.im/entry/582185c42e958a0054feccfb