在哪里使用 callable 以及在哪里使用 Runnable Interface?
Posted
技术标签:
【中文标题】在哪里使用 callable 以及在哪里使用 Runnable Interface?【英文标题】:Where to use callable and where to use Runnable Interface? 【发布时间】:2016-09-24 07:56:33 【问题描述】:我对 Java 还很陌生,我正在研究多线程的概念,在经历使用多线程的各种实现时,我经历了这两个概念。 这个The difference between the Runnable and Callable interfaces in Java 问题说明了两者之间有什么区别以及在哪里使用。
我怀疑 Callable 是否能够做 Runnable 的所有事情,为什么这么多人使用 Runnable 而不是 callable? 与 Runnable 接口相比,实现 Callable 接口是否有额外的开销?
【问题讨论】:
如果你不需要它,你不应该使用 Callable。正如KISS principle 所说。 【参考方案1】:为什么这么多人使用 Runnable 而不是 callable?
Callable 是 jdk 中极具竞争力的新功能,它附带了 Executor 框架,为线程执行提供了更大的灵活性。
在实现 Callable 接口时是否有任何额外的开销 与可运行接口比较?
我不这么认为,如果您遵循任何标准的 Executor 文档,这将非常简单。
【讨论】:
【参考方案2】:一个重要的区别:Runnable
接口中的run()
方法返回void; Callable
接口中的call()
方法返回T
类型的对象。这使您可以轻松访问响应对象。
您可以通过创建私有数据成员并提供 getter 来对 Runnable
的具体实现做同样的事情,但语法糖很甜。
【讨论】:
【参考方案3】:在java.util.concurrent
包出现在Java 5 版本之前,实际上没有其他选择可以进行并发计算,只能直接操作Thread
s。
您可以直接操作线程,例如通过子类化:
public class MyThread extends Thread
public void run()
myComputation();
或者您可以通过创建Runnable
来做同样的事情,这是首选方式(主要是因为您不需要子类化任何东西,这提供了清晰的关注点分离、更好的代码重用或通常更好的组合) :
public class MyRunnable implements Runnable
public void run()
mycomputation();
new Thread(new MyRunnable()).start();
但无论您使用哪种方式,您都必须创建、启动、操作、加入线程。哪个有缺点:您可以创建多少个线程?这个贵吗? (在某些时候确实如此,尽管随着每个 JVM 实现它变得越来越便宜。)
另外,这个 API 本身有一些开发人员必须克服的限制:如果我想从 Runnable
返回一个值,但看到签名仍然不允许呢?我如何知道我的Runnable
是否因Exception
而失败?诸如异常处理程序之类的东西可以通过线程来实现,但这是一个重复的、容易出错的任务。
进入java.util.concurrent
包!它为所有这些(以及更多)提供了答案!
您希望将线程重用于多个“工作单元”(是否是Runnable
?):您可以。想知道“工作单元”是完成还是失败:可以。想要返回一个值:可以。想要动态地将某些任务优先于其他任务?当然。需要安排任务的能力?可以做。以此类推。
用Callable
s 替换您的Runnable
s(这很简单,两者都是单一方法接口!),停止操纵Thread
s 以支持Future
s 并将管道留给@987654335 @。
我怀疑 Callable 是否能够做 Runnable 的所有事情,为什么这么多人使用 Runnable 而不是 callable?
也许他们确实有旧代码(Java 1.4?)。也许他们没有意识到更高级别抽象的好处,而是更喜欢低级别的Thread
东西?
一般来说,既然并发 API 来了,绝对没有理由更喜欢使用Thread
s。
与Runnable接口相比,实现Callable接口是否有额外的开销?
在“实现Callable
”与Runnable
中,没有,没有。但是新的Callable
东西是有成本的,因为在内部,这一切都归结为Thread
s 和Runnable
s(至少,当前的实现是这样做的)。但您实际上可能会获得性能提升,因为有了更简单的线程重用等新功能!
所以,是的,并发 API 有其成本,但它在任何标准用例中绝对完全可以忽略不计,但它也有新的优势,使其在许多方面都变得更好,包括性能。
此外,您还可以从 API 中获得大量新的可能性,如前所述(参见 Fork/Join 框架,参见 Java 8 中的并行流,...),并减少对任何“自定义”扭结的需求,上述功能的“自制”并发代码,众所周知,这很难做到。
收益/成本比完全有利于“新”并发 API。
【讨论】:
【参考方案4】:我怀疑 Callable 是否能够做 Runnable 的所有事情,为什么这么多人使用 Runnable 而不是 callable?与 Runnable 接口相比,实现 Callable 接口是否有额外的开销?
-
使用
Runnable
接口进行即发即弃 调用,尤其是当您对任务执行的结果不感兴趣时。
Callable
接口的实现没有额外的开销。
与文档page的主要区别
Callable
接口类似于Runnable
,因为两者都是为实例可能由另一个线程执行的类设计的。但是,Runnable
不会返回结果,也不会抛出已检查的异常。
让我们查看AbstractExecutorService的源代码
public Future<?> submit(Runnable task)
if (task == null) throw new NullPointerException();
RunnableFuture<Object> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
public <T> Future<T> submit(Callable<T> task)
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
由于Callable
内部使用RunnableFuture<T>
,我看不到执行Callable
任务的任何开销
【讨论】:
【参考方案5】:我怀疑 Callable 是否能够做 Runnable 的所有事情,为什么这么多人使用 Runnable 而不是 callable?
这个问题无异于问:“为什么 Java 有静态类型?”接口是编译时类型签名的声明,不多也不少;任何两个接口之间的唯一区别是参数和返回值的编译时类型。
那么为什么 Java 有静态类型呢?不幸的是,这个问题超出了 *** 的范围。您可以使用 Google 查找有关静态类型语言与动态类型语言的所有信息。
另外,如果您想了解一个不同的世界,您可以尝试编写一些 Ruby 代码。 Ruby 中没有接口,因为没有静态类型。 Ruby 程序员谈论“Duck Typing”,这对 Google 来说是另一个好词。
【讨论】:
以上是关于在哪里使用 callable 以及在哪里使用 Runnable Interface?的主要内容,如果未能解决你的问题,请参考以下文章
在哪里使用 CORBA 以及在哪里使用 SNMP 进行监控?