在Java中设计并发线程时,使用Runnable
和Callable
接口有什么区别?你为什么会选择其中之一?
在Java中设计并发线程时,使用Runnable
和Callable
接口有什么区别?你为什么会选择其中之一?
请参考这里的解释。
Callable接口类似于Runnable,两者都设计用于可能被另一个线程执行的类实例。然而,Runnable不会返回结果,也不能抛出已检查异常。
Runnable
和Callable
在应用程序中的区别是什么?区别只在于Callable
中有返回参数吗?
基本上是这样。请参见此问题的答案以及Callable
的javadoc。
如果
Callable
可以完成所有Runnable
的工作,那么两者的存在有何必要?
因为Runnable
接口不能完成Callable
的全部工作!
Runnable
自Java 1.0就开始存在,但是Callable
是在Java 1.5中引入的...用于处理Runnable
不支持的使用情况。理论上,Java团队可以改变Runnable.run()
方法的签名,但是这将破坏与1.5之前的代码的二进制兼容性,迁移旧Java代码到更新的JVM时需要重新编码,这是绝对不可行的。Java力求向后兼容...这一点一直是Java在商业计算领域最大的卖点之一。
显然,有些用例不需要返回结果或抛出已检查异常。对于这些用例,使用Runnable
比使用Callable<Void>
并从call()
方法返回一个虚拟(null
)值更加简洁。
Runnable
存在(在很大程度上)是为了向后兼容。但是,在某些情况下,实现(或要求)Callable
接口可能是不必要或过于昂贵的(例如,在 ScheduledFuture<?> ScheduledExecutorService.schedule(Runnable command, long delay, TimeUnit unit)
中)。因此,即使历史并没有强制出当前的结果,保持两个接口在语言中仍然有好处吗? - maxRunnable
本来就会被修改。"boilerplate"中的return null;
是一个薄弱的论点。(至少,在你可以忽略向后兼容性的假设情况下,这将是我的决定...) - Stephen CCallable
需要实现call()
方法,而Runnable
需要实现run()
方法。Callable
可以返回值,但Runnable
不能。Callable
可以抛出已检查异常,但Runnable
不能。Callable
可与ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)
方法一起使用,但Runnable
不能。public interface Runnable {
void run();
}
public interface Callable<V> {
V call() throws Exception;
}
让我们看看何时会使用Runnable和Callable。
Runnable和Callable都在不同的线程上运行,而非调用线程。但是Callable可以返回值,而Runnable不能。那么这到底应用在哪里呢。
Runnable:如果您有一个“点火并忘记”任务,则使用Runnable。将代码放入Runnable中,当调用run()方法时,您可以执行任务。调用线程实际上不关心您何时执行任务。
Callable:如果您试图从任务中检索值,则使用Callable。现在仅使用Callable将无法完成工作。您需要将Future包装在Callable周围,并在future.get()上获取值。在此处,调用线程将被阻塞,直到Future返回结果,而后者正在等待Callable的call()方法执行。
因此,请考虑针对目标类的接口,其中定义了两种包装方法:Runnable和Callable。调用类将随机调用您的接口方法,不知道哪个是Runnable,哪个是Callable。Runnable方法将异步执行,直到调用Callable方法为止。在这里,调用类的线程将被阻塞,因为您正在从目标类中检索值。
注意:在目标类内部,您可以在单个线程执行器上调用Callable和Runnable,使该机制类似于串行调度队列。只要调用者调用您的Runnable包装方法,调用线程将非常快速地执行而不会阻塞。一旦它调用一个Future方法中包装的Callable,则必须阻塞,直到所有其他排队项目都被执行。然后该方法将返回值。这是一种同步机制。
Callable
接口声明了call()
方法,你需要提供泛型作为call()
返回的对象类型 -
public interface Callable<V> {
/**
* 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
创建一个线程并在其上调用start()
时,该方法会被调用。你也可以直接调用run()
,但这只会在同一线程中执行run()
方法。
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> 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
接口的类的实例可能由另一个线程执行。Callable
和Runnable
接口的实例都可以被ExecutorService执行。ExecutorService接口中的方法有:
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);
如前所述,Callable是一个相对较新的接口,作为并发包的一部分被引入。Callable和Runnable都可以与执行器一起使用。Thread类(它本身实现了Runnable)仅支持Runnable。
您仍然可以将Runnable与执行器一起使用。Callable的优点在于,您可以将其发送到执行器,并立即获得Future结果,在执行完成后将更新该结果。同样的功能也可以使用Runnable实现,但在这种情况下,您必须自己管理结果。例如,您可以创建保存所有结果的结果队列。其他线程可以等待此队列并处理到达的结果。
Future
,或者添加一个钩子来捕获所有未捕获的异常:http://docs.oracle.com/javase/6/docs/api/java/lang/Thread.html#setDefaultUncaughtExceptionHandler%28java.lang.Thread.UncaughtExceptionHandler%29 - AlexRRunnable
或 Callable
而不是线程池。请参见 Executors.newSingleThreadExecutor()
和 Executors.newSingleThreadScheduledExecutor()
。如果您所说的“线程池”是指 Executors 框架,则应理解 Java 5 中添加 Executors 框架的目的是为了使开发人员无需直接处理 Thread 类。通常情况下,您不再需要扩展 Thread 来实现并发工作。 - Basil BourqueCallable 和 Runnable 的区别如下:
+----------------------------------------+--------------------------------------------------------------------------------------------------+
| Runnable | Callable<T> |
+----------------------------------------+--------------------------------------------------------------------------------------------------+
| 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 generic value V |
| No way to propagate checked exceptions | Callable's call()“throws Exception” clause so we can easily propagate checked exceptions further | |
+----------------------------------------+--------------------------------------------------------------------------------------------------+
Runnable
接口的功能,但他们不想影响Runnable
接口的使用,这可能是为什么他们选择了在Java 1.5中使用名为Callable
的单独接口,而不是更改已经成为Java自Java 1.0以来一部分的现有Runnable
接口。 来源