在哪里使用 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 版本之前,实际上没有其他选择可以进行并发计算,只能直接操作Threads。

您可以直接操作线程,例如通过子类化:

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?):您可以。想知道“工作单元”是完成还是失败:可以。想要返回一个值:可以。想要动态地将某些任务优先于其他任务?当然。需要安排任务的能力?可以做。以此类推。

Callables 替换您的Runnables(这很简单,两者都是单一方法接口!),停止操纵Threads 以支持Futures 并将管道留给@987654335 @。

我怀疑 Callable 是否能够做 Runnable 的所有事情,为什么这么多人使用 Runnable 而不是 callable?

也许他们确实有旧代码(Java 1.4?)。也许他们没有意识到更高级别抽象的好处,而是更喜欢低级别的Thread 东西?

一般来说,既然并发 API 来了,绝对没有理由更喜欢使用Threads。

与Runnable接口相比,实现Callable接口是否有额外的开销?

在“实现Callable”与Runnable 中,没有,没有。但是新的Callable 东西是有成本的,因为在内部,这一切都归结为Threads 和Runnables(至少,当前的实现是这样做的)。但您实际上可能会获得性能提升,因为有了更简单的线程重用等新功能!

所以,是的,并发 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&lt;T&gt;,我看不到执行Callable 任务的任何开销

【讨论】:

【参考方案5】:

我怀疑 Callable 是否能够做 Runnable 的所有事情,为什么这么多人使用 Runnable 而不是 callable?

这个问题无异于问:“为什么 Java 有静态类型?”接口是编译时类型签名的声明,不多也不少;任何两个接口之间的唯一区别是参数和返回值的编译时类型。

那么为什么 Java 有静态类型呢?不幸的是,这个问题超出了 *** 的范围。您可以使用 Google 查找有关静态类型语言与动态类型语言的所有信息。

另外,如果您想了解一个不同的世界,您可以尝试编写一些 Ruby 代码。 Ruby 中没有接口,因为没有静态类型。 Ruby 程序员谈论“Duck Typing”,这对 Google 来说是另一个好词。

【讨论】:

以上是关于在哪里使用 callable 以及在哪里使用 Runnable Interface?的主要内容,如果未能解决你的问题,请参考以下文章

在哪里使用 CORBA 以及在哪里使用 SNMP 进行监控?

什么是编程中的哈希映射以及它可以在哪里使用

在哪里以及如何使用嵌套类? [复制]

我如何以及在哪里使用来自 Apollo 客户端的数据?

如何使用 Kohana ORM 选择以及在哪里 - 如果 - 在?

在哪里以及如何将 nodejs 与 angularjs 一起使用