Executor框架
还记得以前写线程的时候用如下代码:
//初级版中的战斗机
new Thread(()-> System.out.println("12313")).start();
//初级版
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("");
}
}).start();
在后来重新学习多线程时,知道了 JDK 提供了 Executor 框架,可以让我们有效的管理和控制我们的线程,其实质也就是一个线程池。
下面我们来看看 Executor 框架的的关系图:
来看看 Executor 源码发现接口里面只有一个 execute(Runnable r) 方法
public interface Executor {
void execute(Runnable command);
}
虽然 Executor 里十分简单,但是它却为提供了强大且灵活的异步任务执行框架提供了基础,它还提供了一种标准的方式将任务的提交过程和执行过程解耦开来,并用 Runnable 来标识任务。可能有人说夸张了,就一个方法,除了个接收 Runnable 接口的参数方法外,什么都没有。别急,我们再来看看 Executor 接口的子接口。
public interface ExecutorService extends Executor {
//判断线程池中的所有线程是否执行完毕时候
void shutdown();
//关闭当前正在执行的任务,并返回没有执行的任务的清单
List<Runnable> shutdownNow();
//判断是否关闭成功
boolean isShutdown();
//关闭后所有任务都已完成,则返回true
boolean isTerminated();
//检测线程池是否关闭,关闭后返回true
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
//下面是任务提交的方法
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
//剩余的代码是用于任务提交便利方法
}
ExecutorService 接口提供了对生命周期管理的方法,以及可跟踪一个或多个异步任务执行状况返回 Future 的方法。下面我们来写下简单代码看看线程池的生命周期:
/**
* @Auther: Gentle
* @Date: 2019/3/20 14:50
* @Description:
*/
public class TestThreadPool {
//构建线程池
private static final ExecutorService executorService = Executors.newScheduledThreadPool(2);
public static void main(String[] args) throws InterruptedException {
executorService.execute(new MyTask("1"));
executorService.execute(new MyTask("2"));
//关闭线程池
shop();
//关闭线程池,返回未启动任务的清单
// shopNow(); //想测试的话先注释 stop() 方法,开启这个注释
boolean b = executorService.awaitTermination(1000, TimeUnit.SECONDS);
System.out.println(b);
}
//关闭线程池方法
private static void shopNow(){
System.out.println("准备拒绝接收任务");
List<Runnable> runnables = executorService.shutdownNow();
System.out.println(runnables);
}
private static void shop(){
System.out.println("准备拒绝接收任务");
executorService.shutdown();
}
//自定义线程
static class MyTask implements Runnable{
private String name;
public MyTask(String name){
this.name=name;
}
@Override
public void run() {
while (!executorService.isShutdown()){
System.out.println("线程:"+name+"在执行任务");
}
}
}
}
下图是我们使用 shopNow() 方法强制关闭的情况下返回的任务列表。
注意:
ExecutorService 中 awaitTermination() 和 isTerminated() 方法必须在 shutdown()或shutdownNow() 方法后执行,否则用于都返回 false。
上诉接口中涉及到的 Future 接口和 Callable 接口后面的篇章会继续介绍。
Executor框架的两级调度模型
Executor 的两级调度模型,第一次看的时候我也懵了,这是什么鬼?后来看了看了线程实现的关系图才明白。
简单来说,在Windows 系统和 Linux 系统里提供的是一对一的线程模型。一条 Java线程被一对一映射为本地操作系统线程中。当 java 线程启动时也会启动一条操作系统的线程,当线程终止时,对应的操作系统的线程也被回收。线程要执行任务时,操作系统会将线程分配给 CPU。
在我们写的 java 程序中,上层依靠 Executor 框架来调用任务,在下层,操作系统将这些线程分配 CPU 执行。
如下是该框架的两级调度模型图:
Executor框架的执行流程
这里我们先区分一下两种情况。
一种 Executor 框架中 execute() 方法是没有返回值的,调用了就交给线程池处理。
另一种是有返回值的,Executor 框架中 submit() 方法,调用后会返回 Future ,可以取到线程完成后返回的数据。
接下来我们看具体Executor框架的执行流程图:
首先我们在主线程创建好线程池,创建好 Runnable 对象或 Callable 对象。
接下来我们将创建好的对象调用 submit() 或 execute() 提交到 ExecutorServic 管理。
如果使用的是 execute() 方法,任务提交了就提交了。如果是使用的是 submit() 方法,我们可以使用 Future.get() 方法来获得线程执行完毕返回的数据。
由于篇幅有限,今天就先写到这里,接下来会继续深挖线程池。