ExecutorService接口, java.util.concurrent.ExecutorService
, 代表一种可以在后台并发执行的异步执行机制。在这篇ExecutorService
文章中,将解释如何创建ExecutorService
,怎么提交任务并执行,怎么获取这些任务的结果,怎么关闭
ExecutorService
以及再次使用。
任务委托
下图说明了,线程将任务委托给ExecutorService
异步执行:
|
线程将任务委托给 |
一旦线程将任务委托给ExecutorService,线程将独立于该任务的执行继续自己的执行。ExecutorService然后并发地执行任务,独立于提交任务的线程。
Java ExecutorService例子
在深入了解ExecutorService
之前,先看看一个简单的例子,下面是一个简单的ExecutorService例子:
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new Runnable() {
public void run() {
System.out.println("Asynchronous task");
}
});
executorService.shutdown();
首先,用Executos newFixedThreadPool()工厂方法创建一个 ExecutorService
,创建固定10个线程任务的线程池。
其次,通过execute()
加入了匿名实现了Runnable
接口的类,这会导致Runnable由ExecutorService中的一个线程执行。
本文将会有更多样子来说明怎么使用ExecutorService
,这个例子知识为了快速了解通过ExecutorService怎么在后台执行任务。
ExecutorService实现
ExecutorService
非常类似于线程池。实际上
, 目前在java.util.concurrent
包中
实现了ExecutorService
接口的类同时也是线程池的实现类。
ExecutorService
是个接口,所以需要使用它的实现。 ExecutorService
在
java.util.concurrent
包中的实现
:
创建 ExecutorService
怎么创建 ExecutorService
取决于用它的哪个实现。当然也可以用Executors
工厂类创建ExecutorService
实例,下面是一些创建ExecutorService
的例子:
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
ExecutorService executorService2 = Executors.newFixedThreadPool(10);
ExecutorService executorService3 = Executors.newScheduledThreadPool(10);
ExecutorService用法
下面是使用ExecutorService
不同的方法委托任务执行:
- execute(Runnable)
- submit(Runnable)
- submit(Callable)
- invokeAny(...)
- invokeAll(...)
下面会介绍这些方法。
执行Runnable
ExecutorService
execute(Runnable)
方法需要
一个java.lang.Runnable
对象, 并且异步执行,下面是通过ExecutorService
执行
Runnable
:
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new Runnable() {
public void run() {
System.out.println("Asynchronous task");
}
});
executorService.shutdown();
这个方式不能获取到执行的结果,如果需要,有个用Callable
(下面会介绍)。
提交Runnable
ExecutorService
submit(Runnable)
方法同样需要
Runnabl
的实现
, 但是返回一个Future
对象, Future
可以用来检查Runnable
是否执行完成,下面是ExecutorService
submit()
例子:
Future future = executorService.submit(new Runnable() {
public void run() {
System.out.println("Asynchronous task");
}
});
future.get(); //returns null if the task has finished correctly.
submit()
方法返回Java Future(链接查看英文,后续翻译解释,下文同样,就不加说明了) 对象,此对象可以检查Runnable
是否执行完毕。
提交Callable
ExecutorService
submit(Callable)
方法和
submit(Runnable)
方法比较相似,除了使用了 Java Callable 而不是 Runnable
. Callable
和 Runnable
的不同下文会介绍
。
Callable
的结果可以通过submit(Callable)
方法返回的Java Future对象获取,下面是例子:
Future future = executorService.submit(new Callable(){
public Object call() throws Exception {
System.out.println("Asynchronous Callable");
return "Callable Result";
}
});
System.out.println("future.get() = " + future.get());
执行结果:
Asynchronous Callable
future.get() = Callable Result
invokeAny()
invokeAny()
方法的参数是
Callable
对象的集合或者子类对象, 执行这个方法不会返回Future
, 返回会返回其中一个Callable
对象的结果。但是不能保证返回的具体是哪一个Callable
的结果,只是众多中完成的一个。如果其中一个任务完成(或引发异常),其余的Callable
任务将被取消。
下面是代码:
ExecutorService executorService = Executors.newSingleThreadExecutor();
Set<Callable<String>> callables = new HashSet<Callable<String>>();
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 1";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 2";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 3";
}
});
String result = executorService.invokeAny(callables);
System.out.println("result = " + result);
executorService.shutdown();
这段代码打印了 Callable
集合执行返回中的一个结果,我试了多次有时候打印
"Task 1", 有时候打印 "Task 2"。
invokeAll()
invokeAll()
方法调用
Callable
集合作为参数传递的所有可调用对象。invokeAll()返回通过执行每个Callable得到的
Future
对象列表。记住,每个完成的任务可能是因为抛异常,实际上并没有成功。没有办法通过Future
分辨两者的区别。
下面是代码:
ExecutorService executorService = Executors.newSingleThreadExecutor();
Set<Callable<String>> callables = new HashSet<Callable<String>>();
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 1";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 2";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 3";
}
});
List<Future<String>> futures = executorService.invokeAll(callables);
for(Future<String> future : futures){
System.out.println("future.get = " + future.get());
}
executorService.shutdown();
Runnable 和 Callable
Runnable
和Callable非常相似,都代表了一个线程或者ExecutorSerivce可以并发放执行的任务,两个接口都只有一个方法,两者只有一个细小的差别,他们之间的区别可以通过接口定义看出来.
Runnable
接口:
public interface Runnable {
public void run();
}
Callable
接口:
public interface Callable{
public Object call() throws Exception;
}
主要区别是 Runnable
run()
和Callable
call()
,call()
可以返回 Object
,另外不同是 call()
可以 throw 一个 exception, run()
却不能 (除了不受检查的RuntimeException
以及其子类
).
如果需要通过ExecutorService
提交任务并且能有返回的结果,那么需要实现Callable
接口,否则值只要实现Runnable
接口。
取消Task
可以通过调用ExecutorService
返回的Future的
cancel()
取消一个提交到ExecutorService
的任务 (Runnable
or Callable
),只能取消还没开始执行的任务,下面是个例子 :
future.cancel();
ExecutorService Shutdown
使用完Executorservice之后,应该关闭它,这样线程就不会继续运行了。如果你的应用程序是通过main()方法启动的,并且你的主线程退出了你的应用程序,那么如果你的应用程序中有一个活动的ExexutorService,那么该应用程序将继续运行。ExecutorService
中的活动线程阻止JVM关闭此服务
shutdown()
终止ExecutorService
内部的线程可以调用shutdown()
方法,ExecutorService
不会立即关闭,但是不再接受新的任务,一旦当前所有线程执行完毕,ExecutorService
关闭,在调用shutdown()
之前,所有提交到ExecutorService
的任务将被执行,下面看下代码:
executorService.shutdown();
shutdownNow()
如果想立即关闭ExecutorService
,可以调用 shutdownNow()
方法,
将试图立即关闭所有执行的任务,跳过所有已提交但未处理的任务,对于执行的任务没有任何保证,也许他们会停止,也许会执行到最后。这是一个最大的努力尝试。看下调用代码:
executorService.shutdownNow();
awaitTermination()
线程调用 ExecutorService
awaitTermination()
将阻塞,直到其他ExecutorService完全关闭,或者超时。
awaitTermination()
正常在调用
shutdown()
或者 shutdownNow()
之后调用
. 看下面代码:
executorService.shutdown();
executorService.awaitTermination();
参考:http://tutorials.jenkov.com/java-util-concurrent/executorservice.html
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html