【Java线程】Callable和Future
Future模式
Future接口是Java线程Future模式的实现,可以来进行异步计算。
Future模式可以这样来描述:
我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后,我就便可以从Future那儿取出结果。
就相当于下了一张订货单,一段时间后可以拿着提订单来提货,这期间可以干别的任何事情。其中Future接口就是订货单,真正处理订单的是Executor类,它根据Future接口的要求来生产产品。
Callable和Future接口
Callable接口
Callable和Future一个产生结果,一个拿到结果。
Callable接口类似于Runnable,但是Runnable不会返回结果,而Callable可以返回结果,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值。
- V call()
- /**
- * Computes a result, or throws an exception if unable to do so.
- *
- * @return computed result
- * @throws Exception if unable to compute a result
- */
- V call() throws Exception;
Future接口
Future 表示异步计算的结果。Future接口中有如下方法:
- boolean cancel(boolean mayInterruptIfRunning)
取消任务的执行。参数指定是否立即中断任务执行,或者等等任务结束
- boolean isCancelled()
任务是否已经取消,任务正常完成前将其取消,则返回 true
- boolean isDone()
任务是否已经完成。需要注意的是如果任务正常终止、异常或取消,都将返回true
- V get()
等待任务执行结束,然后获得V类型的结果。InterruptedException 线程被中断异常, ExecutionException任务执行异常,如果任务被取消,还会抛出CancellationException
- V get(long timeout, TimeUnit unit)
同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计算超时,将抛出TimeoutException
Future接口提供方法来检测任务是否被执行完,等待任务执行完获得结果。也可以设置任务执行的超时时间,这个设置超时的方法就是实现Java程序执行超时的关键。
所以,如果需要设定代码执行的最长时间,即超时,可以用Java线程池ExecutorService类配合Future接口来实现。
- int result = future.get(5000, TimeUnit.MILLISECONDS);
Future实现类:SwingWorker
http://blog.csdn.net/vking_wang/article/details/8994882
Future实现类:FutureTask
Future的实现类有java.util.concurrent.FutureTask<V>即 javax.swing.SwingWorker<T,V>。通常使用FutureTask来处理我们的任务。
FutureTask类同时又实现了Runnable接口,所以可以直接提交给Thread、Executor执行。
- public class CallableAndFuture {
- public static void main(String[] args) {
- Callable<Integer> callable = new Callable<Integer>() {
- public Integer call() throws Exception {
- return new Random().nextInt(100);
- }
- };
- FutureTask<Integer> future = new FutureTask<Integer>(callable);
- new Thread(future).start();
- try {
- Thread.sleep(5000);// 可能做一些事情
- int result = future.get());
- } catch (InterruptedException e) {
- e.printStackTrace();
- } catch (ExecutionException e) {
- e.printStackTrace();
- }
- }
- }
通过ExecutorService的submit方法执行Callable,并返回Future
使用ExecutorService:
- public class CallableAndFuture {
- public static void main(String[] args) {
- //ExecutorService.submit()
- ExecutorService threadPool = Executors.newSingleThreadExecutor();
- Future<Integer> future = threadPool.submit(new Callable<Integer>() {
- public Integer call() throws Exception {
- return new Random().nextInt(100);
- }
- });
- try {
- Thread.sleep(5000);// 可能做一些事情
- int result = future.get()); //Future.get()
- } catch (InterruptedException e) {
- e.printStackTrace();
- } catch (ExecutionException e) {
- e.printStackTrace();
- }
- }
- }
如果要执行多个带返回值的任务,并取得多个返回值,可用CompletionService:
CompletionService相当于Executor加上BlockingQueue,使用场景为当子线程并发了一系列的任务以后,主线程需要实时地取回子线程任务的返回值并同时顺序地处理这些返回值,谁先返回就先处理谁。
- public class CallableAndFuture {
- public static void main(String[] args) {
- ExecutorService threadPool = Executors.newCachedThreadPool();
- CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(threadPool);
- for(int i = 1; i < 5; i++) {
- final int taskID = i;
- //CompletionService.submit()
- cs.submit(new Callable<Integer>() {
- public Integer call() throws Exception {
- return taskID;
- }
- });
- }
- // 可能做一些事情
- for(int i = 1; i < 5; i++) {
- try {
- int result = cs.take().get()); //CompletionService.take()返回Future
- } catch (InterruptedException e) {
- e.printStackTrace();
- } catch (ExecutionException e) {
- e.printStackTrace();
- }
- }
- }
- }
或者不使用CompletionService:先创建一个装Future类型的集合,用Executor提交的任务返回值添加到集合中,最后便利集合取出数据。
区别:
Future集合方法,submit的task不一定是按照加入自己维护的list顺序完成的。从list中遍历的每个Future对象并不一定处 于完成状态,这时调用get()方法就会被阻塞住,如果系统是设计成每个线程完成后就能根据其结果继续做后面的事,这样对于处于list后面的但是先完成 的线程就会增加了额外的等待时间。
而CompletionService的实现是维护一个保存Future对象的BlockingQueue。只 有当这个Future对象状态是结束的时候,才会加入到这个Queue中,take()方法其实就是Producer-Consumer中的 Consumer。它会从Queue中取出Future对象,如果Queue是空的,就会阻塞在那里,直到有完成的Future对象加入到Queue中。
所以,先完成的必定先被取出。这样就减少了不必要的等待时间。