Java.util.concurrent.ExecutorService接口代表一种异步执行机制,它能够在后台执行任务。因此ExecutorService与thread pool是非常相似的。事实上,在java.util.package包中ExecutorService的具体实现就是一个线程池的具体实现。
任务委托(Task Delegation)
在创建完ExecutorService后,我们就可以将线程委托给ExecutorService执行。
下面的图片说明了一个线程委托一个任务给ExecutorService进行异步执行:
一旦,线程委托任务给ExecutorService,线程会独立任务的执行而继续自己之后的操作。
ExecutorService分配Tasks
ExecutorService可以执行Runnable和Callable的task。其中Runnable是没有返回值的,而Callable是有返回值的。我们分别看一下两种情况的使用:
Runnable runnableTask = () -> {try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}};Callable<String> callableTask = () -> {TimeUnit.MILLISECONDS.sleep(300);return "Task's execution";};
将task分配给ExecutorService,可以通过调用下面这几个方法将任务委托给ExecutorService:
execute(Runnable)submit(Runnable)submit(Callable)invokeAny(…)invokeAll(…)
execute() 返回值是void,他用来提交一个Runnable task。
executorService.execute(runnableTask);
submit() 返回值是Future,它可以提交Runnable task, 也可以提交Callable task。 提交Runnable的有两个方法:
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
第一个方法在返回传入的result。第二个方法返回null。
再看一下callable的使用:
Future<String> future =
executorService.submit(callableTask);
invokeAny() 将一个task列表传递给executorService,并返回其中的一个成功返回的结果。
String result = executorService.invokeAny(callableTasks);
invokeAll() 将一个task列表传递给executorService,并返回所有成功执行的结果:
List<Future<String>> futures = executorService.invokeAll(callableTasks);
下面来逐个具体看看这些方法。
execute(Runnable)
execute(Runnable) 方法接受一个java.lang.Runable对象的实例,并异步执行之。下面是一个使用ExecutorService执行Runnable的例子:
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new Runnable() {
public void run() {
System.out.println("Asynchronous task");
}
});
executorService.shutdown();
这种方式不能获得Runnable执行的结果,如果有这种需要,你将要使用Callable。
submit(Runnable)
submit(Runnable) 方法也接收一个Runnable接口的具体实现,并返回一个Future对象。Future对象可以用来检测Runable是否执行完成。
public class Test {
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future future = executorService.submit(new Runnable() {
public void run() {
System.out.println("Asynchronous task");
}
});
System.out.println(future.get()); //returns null if the task has finished correctly.
}
}
//结果:
Asynchronous task
null
submit(Callable)
submit(Callable)方法与submit(Runnable)方法相似,除了接收的参数有所不同。Callable实例非常类似于Runnable,不同的是call方法可以返回一个结果,Runnable.run()方法不能返回一个结果(因为是void类型),就算线程执行完了,成功了future.get()也只是得到null
可以通过submit(Callable)方法返回的Future对象获取Callable的结果。下面是一个使用Callable的例子:
public class Test {
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newSingleThreadExecutor();
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
如果任务执行完成,future.get()方法会返回Callable任务的执行结果。注意,future.get()方法会产生阻塞。
invokeAny(…)
invokeAny()方法接收一个Callable对象或者Callable的子接口实例的集合作为参数,这个方法不会返回Future,但会返回集合中某一个Callable的结果。你不能确定你得到是哪个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集合中一个Callable任务返回的结果。我尝试执行了多次,结果是变化的。有时候是“Task1”,有时候是“Task 2”等。
invokeAll(…)
invokeAll()接收一个Callable对象的集合作为参数,该方法会调用你传给他的集合中的所有Callable对象
invokeAll()会返回一个Future对象的列表,通过这个列表你可以获取每一个Callable执行的结果。
一个任务可能会因为一个异常而结束,因此这时任务并不是真正意义上执行成功了。这在Future上是没有办法来判断的。
invokeAll()处理一个任务的容器(collection),并返回一个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();
ExecutorService和 Fork/Join
java 7 引入了Fork/Join框架。 那么两者的区别是什么呢?
ExecutorService可以由用户来自己控制生成的线程,提供了对线程更加细粒度的控制。而Fork/Join则是为了让任务更加快速的执行完毕。
