Java中为什么需要Callable

在java中有两种创建线程的方法:

一种是继承Thread类,重写run方法:

public class TestMain {public static void main(String[] args) {MyThread t1 = new MyThread();t1.start();}
}
class MyThread extends Thread {public void run() {System.out.println("MyThread running...");}
}
复制代码

第二种是使用Runnable创建一个线程:

public class TestMain {public static void main(String[] args) {Runnable r1 = new Runnable() {@Overridepublic void run() {System.out.println("Thread created with runnable running...");}};Thread t1 = new Thread(r1);t1.start();}
}
复制代码

其实这两种方式,底层都是执行Thread类的run方法:

无论使用这里的哪种方式创建线程,都无法在线程结束时return一个返回值。但是在非常多的场景下,我们都需要在线程执行结束时,将执行的结果封装为一个返回值返回给主线程(或者调用者线程)。因此java在1.5版本时,在java.util.concurrent包引入了Callable接口,用于线程执行完时return一个返回值。

Callable和Runnable的区别

Runnable和Callable都是接口,分别定义如下:

package java.lang;
​
/*** The <code>Runnable</code> interface should be implemented by any* class whose instances are intended to be executed by a thread. The* class must define a method of no arguments called <code>run</code>.* <p>* @since   JDK1.0*/
@FunctionalInterface
public interface Runnable {public abstract void run();
}
复制代码
package java.util.concurrent;
​
/*** A task that returns a result and may throw an exception.* Implementors define a single method with no arguments called* {@code call}.** <p>The {@code Callable} interface is similar to {@link* java.lang.Runnable}, in that both are designed for classes whose* instances are potentially executed by another thread.  A* {@code Runnable}, however, does not return a result and cannot* throw a checked exception.** <p>The {@link Executors} class contains utility methods to* convert from other common forms to {@code Callable} classes.** @see Executor* @since 1.5* @author Doug Lea* @param <V> the result type of method {@code call}*/
@FunctionalInterface
public interface Callable<V> {/*** @return computed result* @throws Exception if unable to compute a result*/V call() throws Exception;
}
复制代码

可以看出,CallableRunnable主要有两点区别:

  1. 有返回值;
  2. 可以抛出异常(这里抛出的异常,会在future.get()时可以通过ExectionException捕获);

因此可以看出,Callable更加实用。这里举个Callable使用的例子:

Callable callable = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {int i = new Random().nextInt(5);try {Thread.sleep(i * 1000);} catch (InterruptedException e) {e.printStackTrace();}return i;}
};
复制代码

虽然Callable接口的call方法可以返回执行结果,但是有两个问题需要解决:

  1. 线程的创建只能通过Runnable,通过Callable又如何创建线程?
  2. 如何获取执行结果?

答案是FutureRunnableFuture

Future和RunnableFuture

Future是一个接口,看下定义:

package java.util.concurrent;
​
/*** A {@code Future} represents the result of an asynchronous* computation.  Methods are provided to check if the computation is* complete, to wait for its completion, and to retrieve the result of* the computation.  The result can only be retrieved using method* {@code get} when the computation has completed, blocking if* necessary until it is ready.  Cancellation is performed by the* {@code cancel} method.  Additional methods are provided to* determine if the task completed normally or was cancelled. Once a* computation has completed, the computation cannot be cancelled.* If you would like to use a {@code Future} for the sake* of cancellability but not provide a usable result, you can* declare types of the form {@code Future<?>} and* return {@code null} as a result of the underlying task.** @see FutureTask* @see Executor* @since 1.5* @author Doug Lea* @param <V> The result type returned by this Future's {@code get} method*/
public interface Future<V> {
​/*** Attempts to cancel execution of this task.  This attempt will* fail if the task has already completed, has already been cancelled,* or could not be cancelled for some other reason. If successful,* and this task has not started when {@code cancel} is called,* this task should never run.  If the task has already started,* then the {@code mayInterruptIfRunning} parameter determines* whether the thread executing this task should be interrupted in* an attempt to stop the task.** <p>After this method returns, subsequent calls to {@link #isDone} will* always return {@code true}.  Subsequent calls to {@link #isCancelled}* will always return {@code true} if this method returned {@code true}.** @param mayInterruptIfRunning {@code true} if the thread executing this* task should be interrupted; otherwise, in-progress tasks are allowed* to complete* @return {@code false} if the task could not be cancelled,* typically because it has already completed normally;* {@code true} otherwise*/boolean cancel(boolean mayInterruptIfRunning);
​/*** Returns {@code true} if this task was cancelled before it completed* normally.** @return {@code true} if this task was cancelled before it completed*/boolean isCancelled();
​/*** Returns {@code true} if this task completed.** Completion may be due to normal termination, an exception, or* cancellation -- in all of these cases, this method will return* {@code true}.** @return {@code true} if this task completed*/boolean isDone();
​/*** Waits if necessary for the computation to complete, and then* retrieves its result.** @return the computed result* @throws CancellationException if the computation was cancelled* @throws ExecutionException if the computation threw an* exception* @throws InterruptedException if the current thread was interrupted* while waiting*/V get() throws InterruptedException, ExecutionException;
​/*** Waits if necessary for at most the given time for the computation* to complete, and then retrieves its result, if available.** @param timeout the maximum time to wait* @param unit the time unit of the timeout argument* @return the computed result* @throws CancellationException if the computation was cancelled* @throws ExecutionException if the computation threw an* exception* @throws InterruptedException if the current thread was interrupted* while waiting* @throws TimeoutException if the wait timed out*/V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}
复制代码

可以看出,Future可以用来表示线程的未来执行结果:一个容器,这个容器内将来存放的是线程的执行结果,线程执行完之前该容器内没有值,但是线程一旦执行成功(Callablecall方法返回之后),就会将结果存入该容器。从Future的接口定义可看出,Future不仅支持阻塞获取执行结果,还支持取消任务的执行,判断任务是否执行完成等。因此通过Future,主线程(或者调用者线程)可以跟进子现场的执行情况。

Callable其实和Runnable很像,都会执行一个任务,只不过Callable可以返回执行的结果。一般将执行结果封装到Future,调用者线程即可以通过Future获取Callable的执行结果了。因此,一般Callable会和Future搭配使用。

但是问题来了:java创建线程,需要Runnable,获取执行结果又需要Future。因此RunnableFuture来了:

可以看出,通过RunnableFuture,既可以创建线程,又可以获取线程的执行结果,当然RunnableFuture也是一个接口,我们一般情况下会使用它的具体实现类FutureTask

那可能又有人要问了,Callable又是如何建立联系的呢?看下FutureTask的使用方式就明白了:

public class TestMain {public static void main(String[] args) {Callable callable = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {int i = new Random().nextInt(5);try {Thread.sleep(i * 1000);} catch (InterruptedException e) {e.printStackTrace();}return i;}};
​/*** callable创建futureTask* FutureTask实现了RunnableFuture接口,因此即是Runnable又是Future* 作为Runnable可以传入Thread创建线程并执行* 作为Future,可以用来获取执行的结果。* 这里创建出来的futureTask对象有人称为"具柄"或者"存根",大家可以理解为用来获取线程执行结果的一个"引用"即可。*/FutureTask<Integer> futureTask = new FutureTask<Integer>(callable);
​// 作为Runnable使用Thread thread = new Thread(futureTask);thread.start();
​try {// 作为Future使用Integer integer = futureTask.get();System.out.println(integer);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}
}
复制代码

因此FutureTaskCallableRunnable的桥梁。

不使用Callable和Future,仅使用Runnable实现相同功能

下面我们看下,如果不使用CallableFuture,仅使用Runnable如何实现返回值。

public class TestMain {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread t1 = new Thread(myRunnable);t1.start();Object o = myRunnable.get();System.out.println(o);}
}
​
class MyRunnable implements Runnable {// 存储执行结果private Object outCome = null;
​@Overridepublic void run() {int i = new Random().nextInt(5);try {Thread.sleep(i * 1000);} catch (InterruptedException e) {e.printStackTrace();}// 存储执行结果outCome = i;// 产出结果后唤醒等待的get方法synchronized (this) {notifyAll();}}
​public synchronized Object get() {while(outCome == null) {try {// 等待产出结果wait();} catch (InterruptedException e) {e.printStackTrace();}}return outCome;}
}
复制代码

可以看出,通过Runnable实现更加麻烦,因此这也体现出了Callable+Future的优势。

本篇博文主要参考了Callable and Future in Java和Future and FutureTask in java,感兴趣的话可以阅读原文。

Java中Callable和Future相关推荐

  1. Java 中这个叫 Future 的东东,你用过嘛?

    前言 创建线程有几种方式?这个问题的答案应该是可以脱口而出的吧 继承 Thread 类 实现 Runnable 接口 但这两种方式创建的线程是属于"三无产品": 没有参数 没有返回 ...

  2. Java多线程-Callable和Future

    Callable和Future出现的原因 创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需 ...

  3. Android中Callable、Future、FutureTask的概念以及几种线程池的使用

    学习线程池必备知识: 在开始介绍线程池之前,先来介绍下Callable和Future的概念,众所周知,Android中实现多线程的方式有两种,实现Runnable接口或者继承一个Thread,但是这两 ...

  4. 夯实Java基础系列11:深入理解Java中的回调机制

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  5. Java中的Runnable、Callable、Future、FutureTask

    Java中存在Runnable.Callable.Future.FutureTask这几个与线程相关的类或者接口,在Java中也是比较重要的几个概念,我们通过下面的简单示例来了解一下它们的作用于区别. ...

  6. java callable用法_在Java中使用Callable和Future

    最近,我们正在开发一个监控仪表板,我们想要ping几个第三方服务器,只是为了检查它们的可用性,或者我们想要调用健康检查. 我们正在对服务器进行一系列api调用,这些服务器的IP,用户名和密码被外部化为 ...

  7. Java中的Runnable、Callable、Future、FutureTask的区别与示例

    原文地址:http://blog.csdn.net/bboyfeiyu/article/details/24851847 --------------------------------------- ...

  8. Java高并发编程:Callable、Future和FutureTask

    1. Callable 泛型接口,用于获取线程执行完的结果,Callable的声明如下 public interface Callable<V> {// 返回 V 类型的结果V call( ...

  9. 15、Java并发编程:Callable、Future和FutureTask

    Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一 ...

最新文章

  1. 正式环境docker部署hyperf_应用部署 - Docker Swarm 集群搭建 - 《Hyperf v1.1.1 开发文档》 - 书栈网 · BookStack...
  2. 计算机科学与技术历史步伐,计算机科学与技术1001班先进班级体申报材料.pdf
  3. C# System.Timers.Timer中的坑,程序异常退出后timer依然运行问题
  4. linux安装Python2.7
  5. python程序员脱单攻略_作为一只程序员,如何脱单?
  6. keras安装_代码详解:构建一个简单的Keras+深度学习REST API
  7. usb设备由生产到使用 java 114979888
  8. java websocket 后台服务器_Unity3D与Java后台TomCat服务器传递数据和文件(1)建立Java服务器...
  9. junit断言_JUnit断言
  10. ffmpeg drawtext居中放置
  11. 05 CardView的基本使用
  12. 构建大型关系数据仓库的十大最佳实践
  13. 在阿里云上试用 Knative 1
  14. chrome启动参数
  15. 如何让mysql数据库支持超大图片
  16. Python:实现collatz sequence考拉兹序列算法(附完整源码)
  17. SpringBoot整合RabbitMQ之Spring事件驱动模型
  18. 清除U盘内所占的隐藏空间(U盘容量突然变小了)
  19. Android app反编译
  20. MFC禁用编辑框输入法

热门文章

  1. AxureRP7.0各类交互效果汇总帖(转)
  2. 开源新生代的成长之路:从校园到开源,需要迈过哪些挑战?
  3. P1427 小鱼的数字游戏(洛谷)
  4. 开源资产/漏洞管理平台使用测评
  5. 在线翻译、计算、学习、去除所有广告的CSDN浏览器助手
  6. 最近面试了不少人,忍不住想聊聊为了面试而学习这件事
  7. excel填充序列_个性化自己的EXCEL设置1—如何自定义填充序列
  8. 基于Android平台的个人日历的设计与开发
  9. Qt元对象系统(Meta-Object)(四)、Moc源代码分析
  10. windows2008 使用windows server backup定时备份文件夹和批处理脚本定时删除文件夹