博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Callable接口、Runable接口、Future接口
阅读量:7298 次
发布时间:2019-06-30

本文共 11814 字,大约阅读时间需要 39 分钟。

1. Callable与Runable区别

Java从发布的第一个版本开始就可以很方便地编写多线程的应用程序,并在设计中引入异步处理。Thread类、Runnable接口和Java内存管理模型使得多线程编程简单直接。

但Thread类和Runnable接口都不允许声明检查型异常,也不能定义返回值。没有返回值这点稍微有点麻烦。不能声明抛出检查型异常则更麻烦一些。

public void run()方法契约意味着你必须捕获并处理检查型异常。即使你小心地保存了异常信息(在捕获异常时)以便稍后检查,但也不能保证这个类(Runnable对象)的所有使用者都读取异常信息。

你也可以修改Runnable实现的getter,让它们都能抛出任务执行中的异常。但这种方法除了繁琐也不是十分安全可靠,你不能强迫使用者调用这些方法,程序员很可能会调用join()方法等待线程结束然后就不管了。

但是现在不用担心了,以上的问题终于在1.5中解决了。Callable接口和Future接口的引入以及他们对线程池的支持优雅地解决了这两个问题。

不管用哪种方式创建线程,其本质都是Callable接口与Runable接口。两者都是可被其它线程执行的任务!!区别是:

(1)Callable规定的方法是call(),而Runnable规定的方法是run()。(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。(3)call()方法可抛出异常,而run()方法是不能抛出异常的。(4)运行Callable任务可拿到一个Future对象。

2.Future

如上所说,Callable任务返回Future对象。即:Callable和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接口来实现。

三个简单的小例子,体会一下:

package com.zyf.Future;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import java.util.concurrent.TimeUnit;import java.util.concurrent.TimeoutException;public class FutureGetTimeOut1 {      public static void main(String[] args){          int timeout = 2;           ExecutorService executor = Executors.newSingleThreadExecutor();          Boolean result = false;             Future
future = executor.submit(new TaskThread("发送请求"));//将任务提交给线程池 try { result = future.get(timeout, TimeUnit.SECONDS); // result = future.get(timeout, TimeUnit.MILLISECONDS); //1 System.out.println("发送请求任务的返回结果: "+result); //2 } catch (InterruptedException e) { System.out.println("线程中断出错。"); future.cancel(true);// 中断执行此任务的线程 } catch (ExecutionException e) { System.out.println("线程服务出错。"); future.cancel(true); } catch (TimeoutException e) {
// 超时异常 System.out.println("超时。"); future.cancel(true); }finally{ System.out.println("线程服务关闭。"); executor.shutdown(); } } static class TaskThread implements Callable
{ private String t; public TaskThread(String temp){ this.t= temp; } public Boolean call() { //for用于模拟超时 for(int i=0;i<999999999;i++){ if(i==999999998){ System.out.println(t+"成功!"); } if (Thread.interrupted()){ //很重要 return false; } } System.out.println("继续执行.........."); return true; } } }
package com.zyf.Future;import java.util.concurrent.*;public class FutureGetTimeOut2 {    public static void main(String[] args) {        final ExecutorService service = Executors.newFixedThreadPool(1);        TaskThread taskThread = new TaskThread();        System.out.println("提交任务...begin");        Future taskFuture = service.submit(taskThread);        System.out.println("提交任务...end");        try {            Object re = taskFuture.get(60000, TimeUnit.MILLISECONDS);// 超时设置            System.out.println(re);        } catch (InterruptedException e) {            e.printStackTrace();        } catch (ExecutionException e) {            e.printStackTrace();        } catch (TimeoutException e) {            System.out.println("超时 取消任务");            taskFuture.cancel(true);            System.out.println("超时 取消任务OK");        } finally {            service.shutdown();        }    }}class TaskThread implements Callable {    public Object call() throws Exception {        String result = "空结果";        try {            System.out.println("任务开始....");            //修改sleep 的值测试超时            Thread.sleep(500);            result = "正确结果";            System.out.println("任务结束....");        } catch (Exception e) {            System.out.println("Task is interrupted!");        }        return result;    }}
package com.zyf.Future;import java.util.concurrent.*;class MyCallable implements Callable {    private int flag = 0;    public MyCallable(int flag) {        this.flag = flag;    }    public String call() throws Exception {        if (this.flag == 0) {            return "flag = 0";        }        if (this.flag == 1) {            try {                while (true) {                    System.out.println("looping.");                    Thread.sleep(2000);                }            } catch (InterruptedException e) {                System.out.println("Interrupted");            }            return "false";        } else {            throw new Exception("Bad flag value!");        }    }}public class FutureGetBlock {        public static void main(String[] args) {        // 定义3个Callable类型的任务        MyCallable task1 = new MyCallable(0);        MyCallable task2 = new MyCallable(1);        MyCallable task3 = new MyCallable(2);        // 创建一个执行任务的服务        ExecutorService es = Executors.newFixedThreadPool(3);        try {            // 提交并执行任务,任务启动时返回了一个Future对象,            // 如果想得到任务执行的结果或者是异常可对这个Future对象进行操作            Future
future1 = es.submit(task1); // 获得第一个任务的结果,如果调用get方法,当前线程会等待任务执行完毕后才往下执行 System.out.println("task1: " + future1.get()); Future
future2 = es.submit(task2); // 等待5秒后,再停止第二个任务。因为第二个任务进行的是无限循环 Thread.sleep(5000); System.out.println("task2 cancel: " + future2.cancel(true)); // 获取第三个任务的输出,因为执行第三个任务会引起异常 // 所以下面的语句将引起异常的抛出 Future
future3 = es.submit(task3); System.out.println("task3: " + future3.get()); } catch (Exception e) { System.out.println(e.toString()); } // 停止任务执行服务 es.shutdownNow(); }}

 

3. Future实现类

3.1 FutureTask

FutureTask是一个RunnableFuture<V>,而RunnableFuture实现了Runnbale又实现了Futrue<V>这两个接口,

public class FutureTask
implements RunnableFuture
public interface RunnableFuture
extends Runnable, Future
{ /** * Sets this Future to the result of its computation * unless it has been cancelled. */ void run();}

另外它还可以包装Runnable和Callable<V>

public FutureTask(Callable
callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable } public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable }

可以看到,Runnable会被Executors.callable()函数转换为Callable类型,即FutureTask最终都是执行Callable类型的任务。该适配函数的实现如下 :

public static 
Callable
callable(Runnable task, T result) { if (task == null) throw new NullPointerException(); return new RunnableAdapter
(task, result); }/** * A callable that runs given task and returns given result */static final class RunnableAdapter
implements Callable
{ final Runnable task; final T result; RunnableAdapter(Runnable task, T result) { this.task = task; this.result = result; } public T call() { task.run(); return result; } }

由于FutureTask实现了Runnable,因此它既可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行。见下面两个例子:

package com.zyf.Future;import java.util.Random;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;public class FutureTaskDemo {    public static void main(String[] args) {        Callable
callable = new Callable
() { public Integer call() throws Exception { return new Random().nextInt(100); } }; FutureTask
future = new FutureTask
(callable); new Thread(future).start(); try { Thread.sleep(1000);// 可能做一些事情 int result = future.get(); System.out.println(result); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }}
package com.zyf.Future;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import java.util.concurrent.FutureTask;public class FutureTaskDemo2 {    static ExecutorService mExecutor = Executors.newSingleThreadExecutor();    public static void main(String[] args) {        futureDemo();    }    static void futureDemo() {        try {            /**             * 提交runnable则没有返回值, future没有数据             */            Future
future = mExecutor.submit(new Runnable() { @Override public void run() { fibc(20); } }); System.out.println("future result from runnable : " + future.get()); /** * 提交Callable, 有返回值, future中能够获取返回值 */ Future
result2 = mExecutor.submit(new Callable
() { @Override public Integer call() throws Exception { return fibc(20); } }); System.out.println("future result from callable : " + result2.get()); /** * FutureTask则是一个RunnableFuture
,即实现了Runnbale又实现了Futrue
这两个接口, * 另外它还可以包装Runnable(实际上会转换为Callable)和Callable *
,所以一般来讲是一个符合体了,它可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行 * ,并且还可以通过v get()返回执行结果,在线程体没有执行完成的时候,主线程一直阻塞等待,执行完则直接返回结果。 */ FutureTask
futureTask = new FutureTask
(new Callable
() { @Override public Integer call() throws Exception { return fibc(20); } }); // 提交futureTask mExecutor.submit(futureTask); System.out.println("future result from futureTask : " + futureTask.get()); mExecutor.shutdown(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } /** * 效率底下的斐波那契数列, 耗时的操作 * * @param num * @return */ static int fibc(int num) { if (num == 0) { return 0; } if (num == 1) { return 1; } return fibc(num - 1) + fibc(num - 2); }}

如果要执行多个带返回值的任务,并取得多个返回值,两种方法:

1.先创建一个装Future类型的集合,用Executor提交的任务返回值添加到集合中,最后便利集合取出数据。

这时候,submit的task不一定是按照加入自己维护的list顺序完成的。从list中遍历的每个Future对象并不一定处于完成状态,这时调用get()方法就会被阻塞住。

如果系统是设计成每个线程完成后就能根据其结果继续做后面的事,这样对于处于list后面的但是先完成的线程就会增加了额外的等待时间。

所以jdk1.8增加了Future接口的另外一个实现类CompletionService

2.CompletionService相当于Executor加上BlockingQueue,使用场景为当子线程并发了一系列的任务以后,主线程需要实时地取回子线程任务的返回值并同时顺序地处理这些返回值,谁先返回就先处理谁。

而CompletionService的实现是维护一个保存Future对象的BlockingQueue。只有当这个Future对象状态是结束的时候,才会加入到这个Queue中,take()方法其实就是Producer-Consumer中的Consumer。它会从Queue中取出Future对象,如果Queue是空的,就会阻塞在那里,直到有完成的Future对象加入到Queue中。

所以,先完成的必定先被取出。这样就减少了不必要的等待时间。

 

转载地址:http://hygjm.baihongyu.com/

你可能感兴趣的文章
java类中的初始化顺序
查看>>
win10远程桌面连接
查看>>
[转]Web Service与WCF区别
查看>>
vs2010 .net4.0 错误 事件的显式接口实现必须使用事件访问器语法
查看>>
BZOJ1090:[SCOI2003]字符串折叠——题解
查看>>
Python网络爬虫-爬取微博热搜
查看>>
js 与或运算符 || && 妙用
查看>>
react-conponent-secondesElapsed
查看>>
DFS 10.1.5.253 1501
查看>>
vue 项目分享
查看>>
smb
查看>>
3.算法-二叉树遍历
查看>>
File类
查看>>
基于层次关联的鲁棒多目标跟踪
查看>>
Python基础---异常
查看>>
动态调用WebService 通用方法Moss 中 传统开发中都可用。
查看>>
【cocos2d-x 024】 LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏
查看>>
概述C# Cast()
查看>>
LeetCode - 9. Palindrome Number
查看>>
IOS的 new Date()格式化问题
查看>>