Executor采用Runnable做为基本的表达形式,虽然Runnable的run方法可以写入日志,写入文件,写入数据库等操做,可是它不能返回一个值,或者抛出一个受检查的异常,有些须要返回值的需求就不能知足了。java
Executor中的任务有四个状态:建立,提交,开始和完成。若是说有些任务执行时间比较长,但愿可以取消该任务,Executor中的任务在未开始前是能够取消的,若是已经开始了,只能经过中断的方式来取消。若是使用Callable和Future的结合,可使用Future的canel方法取消任务,这样就方便多了。数据库
import java.util.concurrent.*; public class Demo1 { public static void main(String args[]) throws Exception { ServiceTask task = new ServiceTask(); ExecutorService executor = Executors.newCachedThreadPool(); Future<Integer> result = executor.submit(task); executor.shutdown(); System.out.println("正在执行任务"); Thread.sleep(1000); System.out.println("task运行结果为:" + result.get()); } } class ServiceTask implements Callable<Integer>{ @Override public Integer call() throws Exception { Thread.sleep(2000); int result = 0; // 假设一个很庞大的计算 for(int i=1;i<100;i++){ for (int j=0;j<i;j++){ result +=j; } } return result; } }
看一下执行结果:编程
这个例子就是一个很是简单的使用Callable和Futute的例子,ServiceTask类实现了Callable接口,并返回一个Integer类型的值。并发
Future<Integer> result = executor.submit(task);这行代码就是构造一个Future。使用其get()方法就能获得最后的运行值。框架
好了看完这一个简单的例子,那就来仔细了解一下它们。ide
来看一下callable的代码:性能
public abstract interface Callable<V> { public abstract V call() throws Exception; }
能够看出它是接口,提到接口就能够明白接口是灵活的,支持传入泛型参数。这个没什么,咱们来重点介绍一下Futurespa
首先来看关于它的介绍
Future提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,若有必要,计算完成前能够阻塞此方法。取消则由 cancel 方法来执行。还提供了其余方法,以肯定任务是正常完成仍是被取消了。线程
来看一下Future的代码日志
public abstract interface Future<V> { public abstract boolean cancel(boolean paramBoolean); public abstract boolean isCancelled(); public abstract boolean isDone(); public abstract V get() throws InterruptedException, ExecutionException; public abstract V get(long paramLong, TimeUnit paramTimeUnit) throws InterruptedException, ExecutionException, TimeoutException; }
提供了五个方法
public abstract boolean cancel(boolean paramBoolean)
试图取消任务的执行(注意是试图),由于存在一些任务已完成、已取消或者由于某些缘由没法取消的因素,存在着取消失败的可能性。
当canel方法起做用时,有两个状况:
1.任务未开始,则该任务将永远不会运行;
2.任务处于执行状态,paramBoolean表示是否采用中断的方式中断线程。
public abstract boolean isCancelled()
若是任务正常取消的,则返回true。
public abstract boolean isDone();
若是任务已完成,则返回 true。 可能因为正常终止、异常或取消而完成,在全部这些状况中,此方法都将返回 true。
(注意若是调用isCanle方法,那么isDone将始终返回true).
public abstract V get() throws InterruptedException, ExecutionException;
重点到了!这是Future获取计算结果的方式之一,使用get方法。(注意这里返回的是Callable中的泛型)
get方法取决于任务的状态(未开始,运行中,已完成),若是任务已经完成,那么get会当即返回或者抛出一个异常;
若是任务没有完成,那么get将阻塞知道任务完成。若是任务抛出了异常,那么get会将该异常封装成ExecutionException抛出。
public abstract V get(long paramLong, TimeUnit paramTimeUnit) throws InterruptedException, ExecutionException, TimeoutException;
若是须要在给定时间后获取计算结果,可使用这个方法,若是超过给定时间以后没有获得计算结果,则抛出TimeoutException。(注意这里返回的是Callable中的泛型)
来看代码:
import java.util.concurrent.*; public class Demo1 { public static void main(String args[]) throws Exception { // 1.先实例化任务对象 ServiceTask task = new ServiceTask(); // 2.实例化Executor框架中的线程池 ExecutorService executor = Executors.newCachedThreadPool(); // 3.使用submit方法将任务提交(返回的是一个Future) Future<Integer> result = executor.submit(task); // 4.记得关闭线程池 executor.shutdown(); System.out.println("正在执行任务"); Thread.sleep(1000); // 5.打印最后的结果 System.out.println("task运行结果为:" + result.get()); } } /** * Callable的实现类 */ class ServiceTask implements Callable<Integer>{ @Override public Integer call() throws Exception { Thread.sleep(2000); int result = 0; // 假设一个很庞大的计算 for(int i=1;i<100;i++){ for (int j=0;j<i;j++){ result +=j; } } return result; } }
运行结果:
接下来咱们来试一下定时取结果:
仍是在原来的代码上修改:
import java.util.concurrent.*; public class Demo1 { public static void main(String args[]) throws Exception { // 1.先实例化任务对象 ServiceTask task = new ServiceTask(); // 2.实例化Executor框架中的线程池 ExecutorService executor = Executors.newCachedThreadPool(); // 3.使用submit方法将任务提交(返回的是一个Future) Future<Integer> result = executor.submit(task); // 4.记得关闭线程池 executor.shutdown(); System.out.println("正在执行任务"); Thread.sleep(1000); // 5.设置定时一秒取结果 System.out.println("task运行结果为:" + result.get(1,TimeUnit.MILLISECONDS)); } } /** * Callable的实现类 */ class ServiceTask implements Callable<Integer>{ @Override public Integer call() throws Exception { //这里睡眠2秒 Thread.sleep(2000); int result = 0; // 假设一个很庞大的计算 for(int i=1;i<100;i++){ for (int j=0;j<i;j++){ result +=j; } } return result; } }
来提早猜测一下,首先设置了定时一秒以后取得结果,可是ServiceTask设置两秒的睡眠时间,理应取结果失败,看一下运行结果:
是的,若是在规定时间内没法取到结果,就会返回TimeoutException。
FutureTask是Future的实现类,它继承了RunnableFuture,RunnableFuture实际上继承了Runnable和Future接口。
来看一下使用如何FutureTask:
import java.util.concurrent.*; public class FutureCallDemo2 { public static void main(String args[])throws Exception{ // 1.先实例化任务对象 FutureTaskService task = new FutureTaskService(); // 2.实例化Executor框架中的线程池 ExecutorService excutor = Executors.newCachedThreadPool(); // 3.直接new一个FutureTask FutureTask<Long> result = new FutureTask<Long>(task); // 4.提交任务 excutor.submit(result); // 5.关闭线程池 excutor.shutdown(); System.out.println("主线程正在执行任务"); System.out.println("task运行结果为:" + result.get()); } } /** * 继承Callable接口 */ class FutureTaskService implements Callable<Long> { @Override public Long call() throws Exception { Thread.sleep(3000); // 10的阶乘 long sum = 1; for (int i = 1; i <= 10; i++) { sum = sum * i; } return sum; } }
用法的话其实差很少。
Future和Callable能够实现异构任务,可是有不少值得考虑的地方。
好比一个类使用了两个任务,一个负责渲染页面,一个负责下载图像。
伪代码以下:
//经过获取图像 List<ImageData>ImageDataList = future.get(); for(ImageData data:ImageDataList ){ //渲染页面 renderPage(data); }
看似并行的执行任务,可是却存在着问题。若是说下载图像的速度远小于渲染页面的速度,那么最终的执行速度就和串行无异了。
因此只有当大量相互独立且同构的任务能够进行并发处理时,才能体现出将任务分到多个任务中带来的性能提高,考虑实际状况再选择使用会带来事半功倍的效果。
本文参考:
Java并发编程实战