在Java中设计并发线程时,使用Runnable接口和Callable接口有什么区别,为什么你会选择其中一个而不是另一个?


当前回答

正如这里已经提到的,Callable是一个相对较新的接口,它是作为并发包的一部分引入的。Callable和Runnable都可以与执行器一起使用。Thread类(实现Runnable本身)只支持Runnable。

您仍然可以将Runnable与执行程序一起使用。Callable的优点是,你可以将它发送给executor,并立即得到Future结果,当执行完成时,Future结果将被更新。Runnable也可以实现同样的功能,但在这种情况下,您必须自己管理结果。例如,您可以创建保存所有结果的结果队列。其他线程可以等待这个队列并处理到达的结果。

其他回答

请看这里的解释。

Callable接口类似于 可运行的,因为两者都是设计出来的 对于实例为的类 可能由另一个人执行 线程。然而,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 |                                                                     |
+----------------------------------------+--------------------------------------------------------------------------------------------------+

Java的设计者觉得有必要扩展Runnable接口的功能,但他们不想影响Runnable接口的使用,这可能就是为什么他们在Java 1.5中使用一个单独的名为Callable的接口,而不是改变已经存在的Runnable接口的原因,因为Runnable接口自Java 1.0以来一直是Java的一部分。源

正如这里已经提到的,Callable是一个相对较新的接口,它是作为并发包的一部分引入的。Callable和Runnable都可以与执行器一起使用。Thread类(实现Runnable本身)只支持Runnable。

您仍然可以将Runnable与执行程序一起使用。Callable的优点是,你可以将它发送给executor,并立即得到Future结果,当执行完成时,Future结果将被更新。Runnable也可以实现同样的功能,但在这种情况下,您必须自己管理结果。例如,您可以创建保存所有结果的结果队列。其他线程可以等待这个队列并处理到达的结果。

让我们看看在哪里可以使用Runnable和Callable。

Runnable和Callable都运行在与调用线程不同的线程上。但是Callable可以返回值,而Runnable不能。那么这到底适用于什么呢?

Runnable:如果你有一个火和忘记任务,那么使用Runnable。将代码放在Runnable中,当run()方法被调用时,就可以执行任务了。调用线程实际上并不关心您何时执行任务。

Callable:如果您试图从任务中检索值,则使用Callable。可调用本身是做不到的。你将需要一个Future,你将它包裹在你的Callable中,并获得关于Future的价值。get()。在这里,调用线程将被阻塞,直到Future返回结果,然后等待Callable的call()方法执行。

因此,考虑一个到目标类的接口,其中定义了Runnable和Callable包装方法。调用类会随机调用你的接口方法,不知道哪个是Runnable,哪个是Callable。Runnable方法将异步执行,直到Callable方法被调用。这里调用类的线程将阻塞,因为您正在从目标类中检索值。

注意:在目标类内部,您可以在单个线程执行器上调用Callable和Runnable,使此机制类似于串行调度队列。因此,只要调用者调用你的Runnable包装方法,调用线程就会非常快地执行而不会阻塞。一旦它调用了一个包装在Future方法中的Callable,它将不得不阻塞,直到所有其他排队的项都被执行。只有这样,该方法才会返回值。这是一种同步机制。

我在另一篇博客中发现了这一点,可以更好地解释这些差异:

虽然这两个接口都是由希望在不同的执行线程中执行的类实现的,但这两个接口之间有一些区别:

Callable<V>实例返回类型为V的结果,而Runnable实例则不会。 Callable<V>实例可能会抛出受控异常,而Runnable实例则不会

Java的设计者觉得有必要扩展Runnable接口的功能,但他们不想影响Runnable接口的使用,这可能就是为什么他们在Java 1.5中使用一个名为Callable的单独接口,而不是修改已经存在的Runnable的原因。