Java线程池ExecutorService 的理解与使用
2016-07-28 10:52
501 查看
接口 java.util.concurrent.ExecutorService 表述了异步执行的机制,并且可以让任务在后台执行。一个 ExecutorService 实例因此特别像一个线程池。事实上,在 java.util.concurrent 包中的 ExecutorService 的实现就是一个线程池的实现。
首先使用 newFixedThreadPool() 工厂方法创建一个 ExecutorService ,上述代码创建了一个可以容纳10个线程任务的线程池。其次,向 execute() 方法中传递一个异步的 Runnable 接口的实现,这样做会让 ExecutorService 中的某个线程执行这个 Runnable 线程。
壹旦线程把任务委托给 ExecutorService,该线程就会继续执行与运行任务无关的其它任务。
ThreadPoolExecutor
ScheduledThreadPoolExecutor
我会在接下来的内容里把每个方法都看壹遍。
使用这种方式没有办法获取执行 Runnable 之后的结果,如果你希望获取运行之后的返回值,就必须使用 接收 Callable 参数的 execute() 方法,后者将会在下文中提到。
方法 submit(Callable) 和方法 submit(Runnable) 比较类似,但是区别则在于它们接收不同的参数类型。Callable 的实例与 Runnable 的实例很类似,但是 Callable 的 call() 方法可以返回一个结果。方法 Runnable.run() 则不能返回结果。
Callable 的返回值可以从方法 submit(Callable) 返回的 Future 对象中获取。如下是一个 ExecutorService Callable 的样例:
上述样例代码会输出如下结果:
如果一个任务运行完毕或者抛出异常,方法会取消其它的 Callable 的执行。
以下是一个样例:
以上样例代码会打印出在给定的集合中的某一个 Callable 的返回结果。我尝试运行了几次,结果都在改变。有时候返回结果是”Task 1”,有时候是”Task 2”,等等。
需要注意的是,任务有可能因为异常而导致运行结束,所以它可能并不是真的成功运行了。但是我们没有办法通过 Future 对象来了解到这个差异。
以下是一个代码样例:
举例来说,如果你的程序通过 main() 方法启动,并且主线程退出了你的程序,如果你还有一个活动的 ExecutorService 存在于你的程序中,那么程序将会继续保持运行状态。存在于 ExecutorService 中的活动线程会阻止Java虚拟机关闭。
为了关闭在 ExecutorService 中的线程,你需要调用 shutdown() 方法。ExecutorService 并不会马上关闭,而是不再接收新的任务,壹但所有的线程结束执行当前任务,ExecutorServie 才会真的关闭。所有在调用 shutdown() 方法之前提交到 ExecutorService 的任务都会执行。
如果你希望立即关闭 ExecutorService,你可以调用 shutdownNow() 方法。这个方法会尝试马上关闭所有正在执行的任务,并且跳过所有已经提交但是还没有运行的任务。但是对于正在执行的任务,是否能够成功关闭它是无法保证 的,有可能他们真的被关闭掉了,也有可能它会壹直执行到任务结束。这是一个最好的尝试。
本文英文原文链接:http://tutorials.jenkov.com/java-util-concurrent/executorservice.html#executorservice-example ,中文译文首发开源中国社区 http://my.oschina.net/bairrfhoinn/blog/177639,转载请注明原始出处。
ExecutorService 样例
这里有一个简单的使用Java 实现的 ExectorService 样例:1 ExecutorService executorService = Executors.newFixedThreadPool(10); 2 3 executorService.execute(new Runnable() { 4 public void run() { 5 System.out.println("Asynchronous task"); 6 } 7 }); 8 9 executorService.shutdown();
首先使用 newFixedThreadPool() 工厂方法创建一个 ExecutorService ,上述代码创建了一个可以容纳10个线程任务的线程池。其次,向 execute() 方法中传递一个异步的 Runnable 接口的实现,这样做会让 ExecutorService 中的某个线程执行这个 Runnable 线程。
任务的委托(Task Delegation)
下方展示了一个线程的把任务委托异步执行的ExecutorService的示意图。壹旦线程把任务委托给 ExecutorService,该线程就会继续执行与运行任务无关的其它任务。
ExecutorService 的实现
由于 ExecutorService 只是一个接口,你壹量需要使用它,那麽就需要提供一个该接口的实现。ExecutorService 接口在 java.util.concurrent 包中有如下实现类:ThreadPoolExecutor
ScheduledThreadPoolExecutor
创建一个 ExecutorService
你可以根据自己的需要来创建一个 ExecutorService ,也可以使用 Executors 工厂方法来创建一个 ExecutorService 实例。这里有几个创建 ExecutorService 的例子:1 ExecutorService executorService1 = Executors.newSingleThreadExecutor(); 2 ExecutorService executorService2 = Executors.newFixedThreadPool(10); 3 ExecutorService executorService3 = Executors.newScheduledThreadPool(10);
ExecutorService 使用方法
这里有几种不同的方式让你将任务委托给一个 ExecutorService:1 execute(Runnable) 2 submit(Runnable) 3 submit(Callable) 4 invokeAny(...) 5 invokeAll(...)
我会在接下来的内容里把每个方法都看壹遍。
execute(Runnable)
方法 execute(Runnable) 接收一个 java.lang.Runnable 对象作为参数,并且以异步的方式执行它。如下是一个使用 ExecutorService 执行 Runnable 的例子:1 ExecutorService executorService = Executors.newSingleThreadExecutor(); 2 3 executorService.execute(new Runnable() { 4 public void run() { 5 System.out.println("Asynchronous task"); 6 } 7 }); 8 9 executorService.shutdown();
使用这种方式没有办法获取执行 Runnable 之后的结果,如果你希望获取运行之后的返回值,就必须使用 接收 Callable 参数的 execute() 方法,后者将会在下文中提到。
submit(Runnable)
方法 submit(Runnable) 同样接收一个 Runnable 的实现作为参数,但是会返回一个 Future 对象。这个 Future 对象可以用于判断 Runnable 是否结束执行。如下是一个 ExecutorService 的 submit() 方法的例子:1 Future future = executorService.submit(new Runnable() { 2 public void run() { 3 System.out.println("Asynchronous task"); 4 } 5 }); 6 //如果任务结束执行则返回 null 7 System.out.println("future.get()=" + future.get()); submit(Callable)
方法 submit(Callable) 和方法 submit(Runnable) 比较类似,但是区别则在于它们接收不同的参数类型。Callable 的实例与 Runnable 的实例很类似,但是 Callable 的 call() 方法可以返回一个结果。方法 Runnable.run() 则不能返回结果。
Callable 的返回值可以从方法 submit(Callable) 返回的 Future 对象中获取。如下是一个 ExecutorService Callable 的样例:
1 Future future = executorService.submit(new Callable(){ 2 public Object call() throws Exception { 3 System.out.println("Asynchronous Callable"); 4 return "Callable Result"; 5 } 6 }); 7 8 System.out.println("future.get() = " + future.get());
上述样例代码会输出如下结果:
1 Asynchronous Callable 2 future.get() = Callable Result
inVokeAny()
方法 invokeAny() 接收一个包含 Callable 对象的集合作为参数。调用该方法不会返回 Future 对象,而是返回集合中某一个 Callable 对象的结果,而且无法保证调用之后返回的结果是哪一个 Callable,只知道它是这些 Callable 中一个执行结束的 Callable 对象。如果一个任务运行完毕或者抛出异常,方法会取消其它的 Callable 的执行。
以下是一个样例:
01 ExecutorService executorService = Executors.newSingleThreadExecutor(); 02 03 Set<Callable<String>> callables = new HashSet<Callable<String>>(); 04 05 callables.add(new Callable<String>() { 06 public String call() throws Exception { 07 return "Task 1"; 08 } 09 }); 10 callables.add(new Callable<String>() { 11 public String call() throws Exception { 12 return "Task 2"; 13 } 14 }); 15 callables.add(new Callable<String>() { 16 public String call() throws Exception { 17 return "Task 3"; 18 } 19 }); 20 21 String result = executorService.invokeAny(callables); 22 23 System.out.println("result = " + result); 24 25 executorService.shutdown();
以上样例代码会打印出在给定的集合中的某一个 Callable 的返回结果。我尝试运行了几次,结果都在改变。有时候返回结果是”Task 1”,有时候是”Task 2”,等等。
invokeAll()
方法 invokeAll() 会调用存在于参数集合中的所有 Callable 对象,并且返回一个包含 Future 对象的集合,你可以通过这个返回的集合来管理每个 Callable 的执行结果。需要注意的是,任务有可能因为异常而导致运行结束,所以它可能并不是真的成功运行了。但是我们没有办法通过 Future 对象来了解到这个差异。
以下是一个代码样例:
01 ExecutorService executorService = Executors.newSingleThreadExecutor(); 02 03 Set<Callable<String>> callables = new HashSet<Callable<String>>(); 04 05 callables.add(new Callable<String>() { 06 public String call() throws Exception { 07 return "Task 1"; 08 } 09 }); 10 callables.add(new Callable<String>() { 11 public String call() throws Exception { 12 return "Task 2"; 13 } 14 }); 15 callables.add(new Callable<String>() { 16 public String call() throws Exception { 17 return "Task 3"; 18 } 19 }); 20 21 List<Future<String>> futures = executorService.invokeAll(callables); 22 23 for(Future<String> future : futures){ 24 System.out.println("future.get = " + future.get()); 25 } 26 27 executorService.shutdown();
ExecuteService 服务的关闭
当使用 ExecutorService 完毕之后,我们应该关闭它,这样才能保证线程不会继续保持运行状态。举例来说,如果你的程序通过 main() 方法启动,并且主线程退出了你的程序,如果你还有一个活动的 ExecutorService 存在于你的程序中,那么程序将会继续保持运行状态。存在于 ExecutorService 中的活动线程会阻止Java虚拟机关闭。
为了关闭在 ExecutorService 中的线程,你需要调用 shutdown() 方法。ExecutorService 并不会马上关闭,而是不再接收新的任务,壹但所有的线程结束执行当前任务,ExecutorServie 才会真的关闭。所有在调用 shutdown() 方法之前提交到 ExecutorService 的任务都会执行。
如果你希望立即关闭 ExecutorService,你可以调用 shutdownNow() 方法。这个方法会尝试马上关闭所有正在执行的任务,并且跳过所有已经提交但是还没有运行的任务。但是对于正在执行的任务,是否能够成功关闭它是无法保证 的,有可能他们真的被关闭掉了,也有可能它会壹直执行到任务结束。这是一个最好的尝试。
本文英文原文链接:http://tutorials.jenkov.com/java-util-concurrent/executorservice.html#executorservice-example ,中文译文首发开源中国社区 http://my.oschina.net/bairrfhoinn/blog/177639,转载请注明原始出处。
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序