在Java中设计并发线程时使用Runnable
和Callable
接口有什么区别,为什么要选择一个而不是另一个呢?
请看这里的解释.
Callable接口类似于Runnable,因为它们都是为其实例可能由另一个线程执行的类而设计的.但是,Runnable不会返回结果,也不会抛出已检查的异常.
Runnable
和和的应用有什么不同Callable
.区别仅在于返回参数Callable
?
基本上,是的.查看此问题的答案.和javadocCallable
.
如果
Callable
可以做到这两点的话,有什么需要Runnable
?
因为Runnable
界面不能做所有的事情Callable
!
Runnable
自Java 1.0以来一直存在,但Callable
仅在Java 1.5中引入...来处理Runnable
不支持的用例.从理论上讲,Java团队可能已经改变了Runnable.run()
方法的签名,但这会破坏与1.5之前的代码的二进制兼容性,在将旧的Java代码迁移到新的JVM时需要重新编码.这是一个很大的不 - 不.Java努力向后兼容......这也是Java商业计算的最大卖点之一.
而且,显然,有一些用例,其中任务不需要返回结果或抛出已检查的异常.对于那些用例,使用Runnable
比使用Callable
和返回方法中的dummy(null
)值更简洁call()
.
一个Callable
需要实现call()
方法而Runnable
需要实现run()
的方法.
A Callable
可以返回值但不能返回Runnable
.
A Callable
可以抛出已检查的异常,但Runnable
不能.
A Callable
可以与ExecutorService#invokeXXX(Collection extends Callable
方法一起使用但Runnable
不能使用.
public interface Runnable { void run(); } public interface Callable{ V call() throws Exception; }
我在另一篇博客中发现了这一点,可以解释一下这些差异:
虽然这两个接口都是由希望在不同的执行线程中执行的类实现的,但两个接口之间的差异很小:
一个Callable
实例返回类型的结果V
,而一个Runnable
实例不会.
一个Callable
实例可能抛出检查异常,而Runnable
实例不能
Java的设计者觉得需要扩展Runnable
接口的功能,但他们不想影响Runnable
接口的使用,这可能就是为什么他们选择Callable
在Java 1.5中使用一个单独的接口而不是改变已经接口的原因.现有的Runnable
.
让我们看一下使用Runnable和Callable的位置.
Runnable和Callable都运行在与调用线程不同的线程上.但是Callable可以返回一个值,而Runnable则不能.那么这真的适用于哪里呢?
Runnable:如果你有一个fire and forget任务,那么使用Runnable.将代码放在Runnable中,当调用run()方法时,您可以执行任务.执行任务时调用线程实际上并不关心.
可调用:如果您尝试从任务中检索值,则使用Callable.现在可以自行调用不会起作用.你将需要一个围绕Callable的Future,并在future.get()上获取你的值.这里调用线程将被阻塞,直到Future返回结果,而结果又等待Callable的call()方法执行.
因此,考虑一个目标类的接口,其中定义了Runnable和Callable包装方法.调用类会随机调用你的接口方法,不知道哪个是Runnable,哪个是Callable.Runnable方法将异步执行,直到调用Callable方法.这里调用类的线程将阻塞,因为您正在从目标类中检索值.
注意:在目标类中,您可以在单个线程执行程序上调用Callable和Runnable,使此机制类似于串行调度队列.因此,只要调用者调用Runnable包装的方法,调用线程就会非常快速地执行而不会阻塞.一旦它调用了一个Callable裹在Future方法中,它就必须阻塞,直到所有其他排队的项都被执行.只有这样,该方法才会返回值.这是一种同步机制.
Callable
接口声明call()
方法,你需要提供泛型作为Object call()的类型应该返回 -
public interface Callable{ /** * 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; }
Runnable
另一方面,接口声明run()
了在使用runnable创建一个Thread并在其上调用start()时调用的方法.你也可以直接调用run()但是只执行run()方法是同一个线程.
public interface Runnable { /** * When an object implementing interfaceRunnable
is used * to create a thread, starting the thread causes the object's *run
method to be called in that separately executing * thread. ** The general contract of the method
run
is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }
总结一些值得注意的差异是
一个Runnable
而对象不返回结果Callable
对象返回结果.
当Runnable
对象可以抛出异常时,对象不能抛出已检查Callable
的异常.
该Runnable
接口自Java 1.0以来一直存在,而Callable
仅在Java 1.5中引入.
几个相似之处包括
实现Runnable或Callable接口的类的实例可能由另一个线程执行.
ExecutorService可以通过submit()方法执行Callable和Runnable接口的实例.
两者都是功能接口,可以在Java8之后的Lambda表达式中使用.
ExecutorService接口中的方法是
Future submit(Callable task); Future> submit(Runnable task); Future submit(Runnable task, T result);
oracle文档中这些接口的用途:
Runnable接口应由任何其实例要由a执行的类实现Thread
.该类必须定义一个不带参数的方法run
.
可调用:返回结果并可能抛出异常的任务.实现者定义一个没有名为call的参数的方法.该Callable
接口类似于Runnable
,在这两个被设计用于一个其实例潜在地由另一个线程执行.Runnable
但是,A 不会返回结果,也不能抛出已检查的异常.
其他差异:
您可以传递Runnable
创建一个Thread.但是你不能通过传递Callable
参数来创建新的线程.您只能将Callable传递给ExecutorService
实例.
例:
public class HelloRunnable implements Runnable { public void run() { System.out.println("Hello from a thread!"); } public static void main(String args[]) { (new Thread(new HelloRunnable())).start(); } }
使用Runnable
消防和忘记调用.使用Callable
来验证结果.
Callable
可以传递给invokeAll方法,不像Runnable
.方法invokeAny
并invokeAll
执行最常用的批量执行形式,执行一组任务,然后等待至少一个或全部完成
微不足道的差异:要实现的方法名称=> run()
for Runnable
和call()
for Callable
.
正如在此处已经提到的那样,Callable是相对较新的接口,它是作为并发包的一部分引入的.Callable和Runnable都可以与执行程序一起使用.类Thread(实现Runnable本身)仅支持Runnable.
您仍然可以将Runnable与执行程序一起使用.Callable的优点是您可以将其发送给执行程序并立即返回将在执行完成时更新的Future结果.使用Runnable可以实现相同的功能,但在这种情况下,您必须自己管理结果.例如,您可以创建将保存所有结果的结果队列.其他线程可以在此队列上等待并处理到达的结果.
+-------------------------------------+--------------------------------------------------------------------------------------------------+ | Runnable | Callable| +-------------------------------------+--------------------------------------------------------------------------------------------------+ | Introduced in Java 1.0 of java.lang | Introduced in Java 1.5 of java.util.concurrent library | | Runnable cannot be parametrized | Callable is a parametrized type whose type parameter indicates the return type of its run method | | Runnable has run() method | Callable has call() method | | Runnable.run() returns void | Callable.call() returns a value of Type T | | Can not throw Checked Exceptions | Can throw Checked Exceptions | +-------------------------------------+--------------------------------------------------------------------------------------------------+
Java的设计者觉得需要扩展Runnable
接口的功能,但他们不想影响Runnable
接口的使用,这可能就是为什么他们选择Callable
在Java 1.5中使用一个单独的接口而不是改变已经接口的原因.现有的Runnable
接口,自Java 1.0以来一直是Java的一部分.资源
Callable和Runnable之间的区别如下:
JDK 5.0中引入了Callable,而JDK 1.0中引入了Runnable。
Callable具有call()方法,而Runnable具有run()方法。
Callable具有可以返回值的call方法,而Runnable具有可以不返回任何值的run方法。
调用方法可以引发检查异常,但运行方法不能引发检查异常。
可调用的使用commit()方法放入任务队列,而Runnable的使用execute()方法放入任务队列。