我是Java新手,我正在阅读多线程的概念,在进行多线程的各种实现时,我经历了这两个概念。 这个The difference between the Runnable and Callable interfaces in Java问题指出了两者之间的区别以及使用的位置。
我怀疑Callable是否能够执行Runnable所做的一切,为什么这么多人使用Runnable而不是callable? 与Runnable Inteface相比,实现Callable接口是否有额外的开销?
答案 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
内容需要付出代价,因为在内部,它都会回到Thread
和Runnable
(至少,当前的实现会这样做)。但是你可能实际上获得了性能,因为新的功能就像线程重用更容易!
所以,是的,并发API有它的成本,但它在任何标准用例中绝对,完全可以忽略不计,但它也有新的优势,可以在很多方面使它更好,包括性能。
另外,如前所述,您可以从API获得大量新的可能性(请参阅Fork / Join框架,请参阅Java 8中的并行流,...),并减少对"的任何扭结的需求。定制","自制"所述功能的并发代码,这是众所周知的难以正确的。
利益/成本比率完全有利于"新的"并发API。
答案 1 :(得分:4)
我怀疑Callable是否能够执行Runnable所做的一切,为什么这么多人使用Runnable而不是callable?与Runnable Inteface相比,实现Callable接口是否有额外的开销?
Runnable
界面进行 触发并忘记 调用,尤其是当您对任务执行结果不感兴趣时。 Callable
接口的实现没有额外的开销。与文档page
的主要区别的源代码
Callable
接口类似于Runnable
,因为它们都是为其实例可能由另一个线程执行的类而设计的。但是,Runnable
不返回结果,也不能抛出已检查的异常。
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的另一个好词。