Java ExecutorService
Java ExecutorService接口java.util.concurrent.ExecutorService表示一种异步执行机制,该机制能够在后台并发执行任务。在此Java ExecutorService教程中,我将说明如何创建ExecutorService,如何向其提交执行任务,如何查看这些任务的结果以及在需要时如何再次关闭ExecutorService。 。
Java ExecutorService视频教程
如果我们喜欢视频,我在这里有JavaExcecutorService
的视频介绍:
任务委托
一旦线程将任务委托给ExecutorService,线程将继续自己的执行,而与该任务的执行无关。然后,ExecutorService
并发执行任务,而与提交任务的线程无关。
Java ExecutorService示例
在我们深入了解ExecutorService之前,让我们看一个简单的示例。这是一个简单的JavaExecutorService
示例:
ExecutorService executorService = Executors.newFixedThreadPool(10); executorService.execute(new Runnable() { public void run() { System.out.println("Asynchronous task"); } }); executorService.shutdown();
首先使用ExecutorsnewFixedThreadPool()
工厂方法创建一个ExecutorService。这将创建一个具有10个执行任务的线程的线程池。
其次,将Runnable接口的匿名实现传递给execute()方法。这将导致" Runnable"由" ExecutorService"中的线程之一执行。
在本教程中,我们还将看到更多有关如何使用ExecutorService的示例。这个例子只是让我们快速了解如何使用ExecutorService在后台执行任务。
Java ExecutorService实现
Java的ExecutorService与线程池非常相似。实际上,存在于java.util.concurrent包中的ExecutorService接口的实现是线程池实现。如果我们想了解如何在内部实现ExecutorService接口,请阅读上述教程。
由于ExecutorService
是一个接口,因此我们需要对其实现进行任何使用。 ExecutorService在java.util.concurrent包中具有以下实现:
- 线程池执行器
- ScheduledThreadPoolExecutor
创建一个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(...)
在以下各节中,我将介绍每种方法。
执行可运行
Java ExecutorServiceexecute(Runnable)方法采用一个java.lang.Runnable对象,并异步执行它。这是一个用
ExecutorService执行
Runnable`的例子:
ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.execute(new Runnable() { public void run() { System.out.println("Asynchronous task"); } }); executorService.shutdown();
如有必要,无法获得已执行的" Runnable"的结果。为此,我们将必须使用"可调用"(在以下各节中进行说明)。
提交可运行
Java的ExecutorService的submit(Runnable)方法也采用了Runnable的实现,但是返回了一个Future对象。这个Future
对象可以用来检查Runnable
是否完成执行。
这是一个JavaExecutorService``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完成的时间。
提交可致电
Java ExecutorService的submit(Callable)方法类似于submit(Runnable)方法,只是它采用Java Callable而不是Runnable。稍后将解释"可调用"与"可运行"之间的精确区别。
可通过submit(Callable)方法返回的Java Future对象获得Callable的结果。这是一个ExecutorService``Callable
示例:
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对象或者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"之一返回的对象。我尝试运行了几次,结果改变了。有时是"任务1",有时是"任务2",等等。
invokeAll()
invokeAll()方法调用作为参数传递的集合中传递给它的所有Callable对象。 invokeAll()返回一个Future对象的列表,通过它可以获取每个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"; } }); List<Future<String>> futures = executorService.invokeAll(callables); for(Future<String> future : futures){ System.out.println("future.get = " + future.get()); } executorService.shutdown();
可运行与可调用
"可运行"界面与"可调用"界面非常相似。这两个接口都表示可以由线程或者ExecutorService并发执行的任务。这两个接口只有一个方法。不过," Callable"和" Runnable"接口之间只有一个小区别。当我们看到接口声明时,将更容易看到" Runnable"和" Callable"接口之间的区别。
首先是" Runnable"接口声明:
public interface Runnable { public void run(); }
这是Callable
接口声明:
public interface Callable{ public Object call() throws Exception; }
Runnable的run()方法和Callable的call()方法之间的主要区别是call()方法可以从方法调用中返回Object。 call()和run()之间的另一个区别是call()可以引发异常,而run()不能引发异常(RuntimeException的未经检查的异常子类除外)。
如果需要向Java ExecutorService提交任务,并且需要任务的结果,则需要使任务实现Callable接口。否则,任务只能实现" Runnable"接口。
取消任务
我们可以通过在提交任务时返回的" Future"上调用" cancel()"方法来取消提交给Java ExecutorService的任务(" Runnable"或者" Callable")。仅当任务尚未开始执行时,才可以取消任务。这是通过调用Future.cancel()
方法取消任务的示例:
future.cancel();
执行器服务关闭
使用完JavaExecutorService
后,应将其关闭,这样线程就不会继续运行。如果应用程序是通过main()方法启动的,而主线程退出了应用程序,那么如果应用程序中有活动的ExexutorService,则该应用程序将继续运行。该ExecutorService
中的活动线程可防止JVM关闭。
关掉()
要终止ExecutorService内部的线程,请调用其Shutdown()方法。 ExecutorService不会立即关闭,但是将不再接受新任务,并且一旦所有线程都完成了当前任务,ExecutorService就会关闭。执行所有在调用Shutdown()之前提交给ExecutorService的任务。这是执行JavaExecutorService
关闭的示例:
executorService.shutdown();
shutdownNow()
如果我们想立即关闭ExecutorService
,可以调用shutdownNow()
方法。这将尝试立即停止所有正在执行的任务,并跳过所有已提交但未处理的任务。不能保证执行任务。也许他们停止了,也许执行到了最后。这是尽力而为的尝试。这是调用ExecutorService``shutdownNow
的示例:
executorService.shutdownNow();
awaitTermination()
ExecutorService的awaitTermination()方法将阻塞调用它的线程,直到ExecutorService完全关闭或者发生给定的超时为止。通常在调用" shutdown()"或者" shutdownNow()"之后调用" awaitTermination()"方法。这是调用ExecutorService``awaitTermination()
的示例:
executorService.shutdown(); executorService.awaitTermination();