在哪里使用可调用以及在哪里使用Runnable接口?

时间:2016-05-26 10:50:25

标签: java runnable callable

我是Java新手,我正在阅读多线程的概念,在进行多线程的各种实现时,我经历了这两个概念。 这个The difference between the Runnable and Callable interfaces in Java问题指出了两者之间的区别以及使用的位置。

我怀疑Callable是否能够执行Runnable所做的一切,为什么这么多人使用Runnable而不是callable? 与Runnable Inteface相比,实现Callable接口是否有额外的开销?

5 个答案:

答案 0 :(得分:6)

在有java.util.concurrent软件包进入Java 5发行版之前,实际上没有其他选项可以进行并发计算,而是直接操作Thread

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

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还是不是?):你可以。想知道"工作单位"完成或失败:你可以。想要返回一个值:你可以。想要将某些任务动态优先于其他任务吗?当然。需要能够安排任务吗?可以做。等等。

Runnable替换为Callable s(这很简单,两者都是单一方法接口!),停止操纵Thread以支持Future s并将管道留给ExecutorService

  

我怀疑Callable是否能够执行Runnable的所有功能,为什么这么多人使用Runnable而不是可调用?

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

一般来说,自从并发API出现以来,绝对没有理由更喜欢使用Thread

  

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

实施Callable" vs Runnable,没有。但是新的Callable内容需要付出代价,因为在内部,它都会回到ThreadRunnable(至少,当前的实现会这样做)。但是你可能实际上获得了性能,因为新的功能就像线程重用更容易!

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

另外,如前所述,您可以从API获得大量新的可能性(请参阅Fork / Join框架,请参阅Java 8中的并行流,...),并减少对"的任何扭结的需求。定制","自制"所述功能的并发代码,这是众所周知的难以正确的。

利益/成本比率完全有利于"新的"并发API。

答案 1 :(得分:4)

  

我怀疑Callable是否能够执行Runnable所做的一切,为什么这么多人使用Runnable而不是callable?与Runnable Inteface相比,实现Callable接口是否有额外的开销?

  1. 使用Runnable界面进行 触发并忘记 调用,尤其是当您对任务执行结果不感兴趣时​​。
  2. Callable接口的实现没有额外的开销。
  3. 与文档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任务没有任何开销,Callable内部使用RunnableFuture<T>

答案 2 :(得分:2)

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

您可以通过创建私有数据成员并提供getter来对Runnable的具体实现执行相同的操作,但语法糖很甜。

答案 3 :(得分:0)

  

为什么这么多人使用Runnable而不是callable?

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

  

在实现Callable接口时是否有额外的开销?   与Runnable Inteface比较?

我不这么认为,如果你遵循任何标准的Executor文档,它是非常直接的。

答案 4 :(得分:0)

  

我怀疑Callable是否能够执行Runnable的所有功能,为什么这么多人使用Runnable而不是可调用?

这个问题等于问及,#34;为什么Java有静态类型?&#34;接口是编译时类型签名的声明,仅此而已;并且任何两个接口之间的唯一区别是参数的编译时类型和返回值。

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

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