Java Executor线程池框架的概述
Posted 刘Java
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java Executor线程池框架的概述相关的知识,希望对你有一定的参考价值。
基于JDK1.8详细介绍了Executor线程池框架的基本架构组成。
文章目录
1 Executor框架的概述
JDK1.5之前,我们如果想要使用Java线程来完成相关任务,一般涉及两个类,一个是Thread类,一个Thread对象在启动(start)之后会创建一个关联的本地操作系统线程,随后会自动回调run方法。另一个是Runnable接口,可以看作 run方法的抽象,代表线程任务。通过Runnable和Thread的配合可以编写我们自己的多线程逻辑。
可以看到,此时Java对于多线程编程的支持还是比较原始的,功能也并不多。因此,在JDK1.5的JUC包中,对Java的多线程应用做了一次全面的扩展,比如新lock锁、并发容器等,还有一个重要的扩展就是出现了Executor执行框架。
Executor执行框架将Java线程的应用做了更细致的功能划分,并且进行了功能的增强,大概包括三个部分:
- 线程任务
- JDK1.5之前,只有Runnable代表线程任务,对于受检异常,必须手动在try catch中处理,不支持throws声明可能抛出的异常,不支持任务返回值。
- JDK1.5的时候,出现了Callable接口,可以看作Runnable的增强:对于受检异常,可以不用在try catch中处理,支持throws声明可能抛出的异常,并且支持任务返回值。
- 执行器
- JDK1.5之前,线程任务的执行需要我们手动创建Thread对象、传入任务、并调用start方法,一个任务对应一个线程,它们之间的关联非常紧密,这样对于线程任务的管理、线程资源的复用等功能几乎没有,或者只能自己手动实现,非常麻烦。
- JDK1.5的时候,出现了Executor线程池。线程池作为任务执行器,我们只需要创建指定的线程池,随后将线程任务传入线程池中,由线程池来确定是将任务直接分配给池中的线程去执行、还是创建线程并执行、或者是加入任务队列等待等等逻辑,使用线程池之后我们不再需要手动创建线程去执行,并且可以实现线程的复用以及线程任务的管理等强大的功能。执行器(线程池)将任务与线程解耦!
- 异步执行结果
- JDK1.5之前,在线程任务启动之后,对于线程任务监控几乎没有,我们不知道任务有没有完成,也没办法定义任务的返回值等一系列信息。
- JDK1.5的时候,出现了Future接口以及它的各种实现。这个接口体系代表了线程任务异步计算的结果,通常与Callable线程任务连用。利用了Future设计模式,在一个线程A执行线程任务的时候,我么可以在另一个线程B中异步的通过Future的实现的相关方法完成判断对应线程任务是否执行完毕、是否被取消、手动取消正在执行的线程任务、以及从执行完毕的线程任务中获取返回值等功能。
有了执行框架,我们只需创建线程任务、然后交给指定的线程池去执行,执行完毕之后等待获取返回结果即可,不再需要关注线程的创建、开启、执行、回收等基础性的中间工作,将任务与线程解耦,程序员更加的关注线程任务本身(这里是和业务相关的),有利于多线程程序的开发!
如果想要使用执行框架,只需要看相关api文档即可!那么我们有必要深入理解执行框架吗?当然时间充足的情况下是有必要的,只有我们知道了执行框架的原理之后,才能更好的使用它。执行框架属于JDK自带的基础框架,经历了时间和众多Java开发者的考验,不求能够手写,仅仅学习它的设计精华,包括各种设计模式,同时避开隐藏的坑,对于程序员个人的后续职业发展也是具有很大帮助的!
线程任务、执行器、执行结果这三部分,都可以围绕着Executor线程池展开,因此下面我们将从Executor入手,并且会穿插介绍Callable和Future的相关原理。
2 Executor线程池的概述
在上面关于Executor执行框架的概述中我们就说过,线程池作为连接线程任务和异步执行结果的执行器,其重要性不言而喻。
在Java框架设计中,一般涉及到资源相关的,并且资源具有创建消耗大、可复用的特点时,都采用了池化技术管理资源,形成一个“资源池”,池化技术可以带来以下一般性好处:对外部隐藏了资源的创建与释放的细节、实现资源的复用减少内存或者时间性能开销。常见Java中池化技术有:数据库连接池(管理数据库连接资源)、基本类型包装类中的缓存池(管理常用包装类对象)、Http连接池(管理http连接资源)、redis连接池(管理redis连接)等等,当然还包括马上要讲的线程池。
Executor线程池来自于JDK1.5的JUC包,使用线程池的目的或者好处如下:
- 实现线程资源的合理复用。线程资源属于操作系统核心资源之一,创建和销毁都需要占用系统资源和大量时间。使用线程池之后,不再需要开发者管理线程,线程的创建和销毁都交给线程池控制,实现线程的复用,减少线程资源的频繁的创建和销毁。
- 提升任务执行效率。当新来一个线程任务的时候,由于具有线程复用计数因此可以直接利用现有的线程去执行任务,不需要新建线程,这样一定程度上提升了执行效率。
- 可以对线程和线程任务实现实时监控和管理。比如目前活动线程数、曾经的最大线程数、已完成的任务数量等功能;比如控制最大线程数,在线程任务执行前-执行完毕后-线程池停止后具有可选的回调方法、移除某个线程任务、立即停止线程池等功能,他们都可以通过线程池的相关方法调用来实现。
JDK的线程池可扩展性极强,我们既可以利用定义好的线程池,也可以自定义线程池,很多其他框架组件也都是使用或者扩展了JDK线程池,比如ForkJoinPool分治框架(线程池框架的增强),guava的线程池MoreExecutors就是基于JDK线程池做的扩展,权限框架Shiro的PropertiesRealm属性文件认证类,JAVA RESTFUL请求服务框架Jersey,甚至单元测试框架junit等等框架都是用到了原生Executor线程池,下面来看看JDK的Executor线程池的主要原理吧!
3 Executor线程池的基本结构
从JDK1.5到JDK1.8,线程池的成员已经扩展的非常多了,下面仅仅列举了常用的关键的类和接口的uml关系:
下面先简单介绍核心接口以及实现类:
- Executor
- 作为线程池的顶级执行接口,也是一个函数式接口。只有一个execute方法,用于执行已提交的 Runnable 任务对象。
- 它不仅仅是一个接口,更是代表着一种将任务与每个任务将如何运行的机制(包括线程的创建、使用、调度等)分离开来的思想。使用者只需要提交任务,不需要创建线程,执行的细节被封装到Executor中,任务的执行方法可以根据实现者自由选择,可以实现为异步(使用新线程执行任务)、也可以是同步的(在调用者的线程中立即运行已提交的任务)。
- ExecutorService
- 继承并扩展了Executor接口的执行服务接口。
- 新增了可为跟踪一个或多个异步任务执行状况而生成 Future 的方法,比如submit方法,作为execute方法的扩展。新增了可以关闭线程池的方法,比如shutdown和shutdownNow方法。新增了批量执行任务的方法,比如 invokeAny 和 invokeAll方法。
- AbstractExecutorService
- 实现了ExecutorService的抽象类,提供 ExecutorService 执行方法的默认实现。比如对ExecutorService返回Future,实现为返回RunnableFuture。另一个作用是作为骨干实现最大限度地减少ExecutorService的实现类的代码。
- ThreadPoolExecutor
- 继承了ExecutorService的普通类,这是JDK线程池的核心实现。
- 它的构造器提供了各种可配置参数,比如线程数量、任务队列、拒绝策略等,方便我们自定义自己的线程池,以及各种钩子 (hook) 方法,方便追踪线程任务的执行,这是我们学习的重点,这里不做详细介绍。
- ScheduledThreadPoolExecutor
- 继承了ThreadPoolExecutor的普通类,可以看作功能的扩展或增强。
- 它能够将线程任务延迟指定时间后执行,或者间隔固定时间多次执行。功能与Timer类似,但ScheduledThreadPoolExecutor功能更强大、更灵活。Timer对应的是单个后台线程,而ScheduledThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数。Timer中一个任务出现异常之后会影响其他任务的执行,但是ScheduledThreadPoolExecutor不会。Timer中一个任务耗时较常会影响其他任务的执行,ScheduledThreadPoolExecutor不会。
- Executors
- 独立出来的一个普通类(没有继承和实现关系,采用组合/聚合关系,图上没有注明),作为一个线程池工厂,提供各种实用方法。
- 提供了各种预定义线程池的实现,比如CachedThreadPool、FixedThreadPool等;提供了将Runnable包装、转换为Callable的方法;提供默认的ThreadFactory线程工厂的实现等功能。
后面的文章我们主要学习ThreadPoolExecutor、ScheduledThreadPoolExecutor、Executors这三个类使用以及原理。另外JDK1.7的时候线程池新增了ForkJoinPool分治框架,这是对线程池的增强,后面的文章我们会讲解ForkJoinPool的源码!
以上是关于Java Executor线程池框架的概述的主要内容,如果未能解决你的问题,请参考以下文章