哪里用callable,哪里用Runnable接口?

Where to use callable and where to use Runnable Interface?

我对 Java 很陌生,我正在研究多线程的概念,在研究使用多线程的各种实现时,我研究了这两个概念。 这个 The difference between the Runnable and Callable interfaces in Java 问题指定了两者之间的区别以及使用位置。

我的疑问是,如果 Callable 能够完成 Runnable 的所有事情,为什么那么多人使用 Runnable 而不是 callable? 与 Runnable 接口相比,实现 Callable 接口是否有额外的开销?

why so many people use Runnable instead of callable?

Callable 是 jdk 中具有竞争力的新功能,它与 Executor 框架一起提供,为线程执行提供了更大的灵活性。

Are there any extra overheads in implementing Callable interface in comparison with Runnable Inteface?

我不这么认为,如果您遵循任何标准的 Executor 文档,这将非常简单。

一个重要区别:Runnable 接口中的 run() 方法 returns void; Callable 接口中的 call() 方法 returns 类型 T 的对象。这使您可以轻松访问响应对象。

您可以通过创建私有数据成员并提供 getter,使用 Runnable 的具体实现来做同样的事情,但语法糖很甜。

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 本身有开发人员必须克服的限制:如果我想 return 来自 Runnable 的值怎么办,看到签名不允许它反正?我怎么知道我的 Runnable 是否因 Exception 而失败?诸如异常处理程序之类的东西允许使用线程,但它是一个重复的、容易出错的任务。

进入java.util.concurrent包!它提供了所有这些(以及更多)的答案!

您想为多个 "unit of work" 重用线程(是否 Runnable?):可以。想知道 "unit of work" 是完成还是失败:可以。想要return一个值:可以。想要动态地将某些任务优先于其他任务?当然。需要安排任务的能力?可以做。等等。

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

My doubt is if Callable is capable of doing everything that Runnable, why so many people use Runnable instead of callable?

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

一般来说,绝对没有理由更喜欢使用 Thread,因为并发 API 来了。

Are there any extra overheads in implementing Callable interface in comparison with Runnable Interface?

在"implementing Callable" vs Runnable中,没有none。但是新的 Callable 东西是有代价的,因为在内部,这一切都归结为 Threads 和 Runnables(至少,当前的实现是这样做的)。但是您实际上可能会获得性能提升,因为新功能(例如更容易的线程重用)!

所以,是的,并发 API 有它的成本,但它在任何标准用例中绝对、完全可以忽略不计,但它也有新的优势,使它在许多方面变得更好,包括性能。

此外,如前所述,您可以从 API 中获得大量新的可能性(参见 Fork/Join 框架,参见 Java 8 中的并行流,...),并减少对上述功能的 "custom"、"self made" 并发代码的任何纠结的需要,这是出了名的难以正确实现。

benefit/cost 比率完全有利于 "new" 并发 API。

My doubt is if Callable is capable of doing everything that Runnable, why so many people use Runnable instead of callable? Are there any extra overheads in implementing Callable interface in comparison with Runnable Inteface?

  1. 使用 Runnable 接口进行 即发即忘 调用,尤其是当您对任务执行的结果不感兴趣时.
  2. Callable 接口的实现没有额外的开销。

与文档的主要区别page

The Callable interface is similar to Runnable, in that both are designed for classes whose instances are potentially executed by another thread. A Runnable, however, does not return a result and cannot throw a checked exception.

让我们检查一下 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 任务的任何开销,因为 Callable 内部使用 RunnableFuture<T>

My doubt is if Callable is capable of doing everything that Runnable, why so many people use Runnable instead of callable?

这个问题等于在问,"Why does Java have static types?"接口就是编译时类型签名的声明,仅此而已;任何两个接口之间的唯一区别是参数的编译时类型和 return 值。

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

此外,如果你想一睹不同的世界,你可以尝试编写一些 Ruby 代码。 Ruby 中没有接口,因为没有静态类型。 Ruby 程序员谈论 "Duck Typing",这是 Google 的另一个好词。