Java中的Runnable和Callable接口的区别

在Java中devise并发线程时,使用Runnable和Callable接口有何区别,为什么要select一个呢?

在这里看到解释。

Callable接口与Runnable相似,都是为其实例可能由另一个线程执行的类devise的。 但是,Runnable不会返回结果,也不会抛出检查的exception。

RunnableCallable的应用程序有什么不同? 与Callable的返回参数只有区别吗?

基本上,是的。 看到这个问题的答案。 还有Callable的javadoc 。

如果Callable可以完成Runnable所有工作,那么两者都需要什么?

因为Runnable接口不能完成Callable所做的一切!

Runnable自Java 1.0以来一直存在,但Callable仅在Java 1.5中引入…来处理Runnable不支持的用例。 理论上,Java团队可能已经改变了Runnable.run()方法的签名,但是这会破坏1.5之前的代码的二进制兼容性,在将旧的Java代码迁移到较新的JVM时需要重新编码。 这是一个很大的NO-NO。 Java努力向后兼容…这是Java商业计算最大的卖点之一。

而且,很明显,有一些用例不需要返回结果或抛出一个检查的exception。 对于这些用例,使用Runnable比使用Callable<Void>更简洁,并从call()方法返回一个虚拟( null )值。

  • Callable需要实现call()方法,而Runnable需要实现run()方法。
  • Callable可以返回一个值,但Runnable不能。
  • Callable可以抛出checkedexception,但Runnable不能。
  • Callable可以与ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)方法一起使用,但Runnable不能。

     public interface Runnable { void run(); } public interface Callable<V> { V call() throws Exception; } 

我在另一个博客中发现了这个问题,可以解释一下这些差异 :

尽pipe这两个接口都是由希望在不同的执行线程中执行的类来实现的,但是两个接口之间几乎没有什么区别:

  • Callable<V>实例返回typesV的结果,而Runnable实例则不返回。
  • Callable<V>实例可能会抛出检查exception,而Runnable实例不能

Java的devise者觉得需要扩展Runnable接口的function,但是他们不想影响Runnable接口的使用,也许这就是为什么他们在Java 1.5中有一个名为Callable的独立接口的原因改变已经存在的Runnable

让我们看看在哪里使用Runnable和Callable。

Runnable和Callable都在与调用线程不同的线程上运行。 但Callable可以返回一个值,Runnable不能。 那么这真的适用于哪里?

可运行的 :如果你有一个消防和忘记的任务,然后使用可运行。 把你的代码放到一个Runnable中,当run()方法被调用时,你可以执行你的任务。 调用线程在执行任务时并不在乎。

Callable :如果您试图从任务中检索值,则使用Callable。 现在可以自行调用不会做这个工作。 你将需要一个Future来包装你的Callable,并在future.get()上获取你的值。 这里的调用线程将被阻塞,直到Future返回结果,而结果又等待Callable的call()方法执行。

因此,想一下在定义了Runnable和Callable包装方法的目标类的接口。 调用类将随机调用不知道哪些是可运行的,哪些是可调用的接口方法。 Runnable方法将asynchronous执行,直到调用Callable方法。 这里调用类的线程将被阻塞,因为您正在从目标类中检索值。

注意:在你的目标类中你可以调用一个线程执行器上的Callable和Runnable,这个机制类似于一个串行调度队列。 所以只要调用者调用你的Runnable包装方法,调用线程将会执行得非常快而没有阻塞。 只要它调用Future方法中的Callable,它将不得不阻塞,直到所有其他排队的项目被执行。 只有这样方法才会返回值。 这是一个同步机制。

Callable接口声明了call()方法,你需要提供generics作为对象的调用types()应该返回 –

 public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; } 

另一方面, Runnable是声明run()方法的接口,当你使用runnable创build一个Thread并调用start()时调用run()方法。 你也可以直接调用run(),但是只是执行run()方法是同一个线程。

 public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); } 

总结几个显着的差异是

  1. Runnable对象不返回结果,而Callable对象返回结果。
  2. 一个Runnable对象不能抛出一个检查exception,当一个Callable对象可以抛出一个exception。
  3. 自Java 1.0以来, Runnable接口就已经存在,而Callable仅在Java 1.5中引入。

几个相似之处包括

  1. 实现Runnable或Callable接口的类的实例可能由另一个线程执行。
  2. Callable和Runnable接口的实例可以由ExecutorService通过submit()方法执行。
  3. 两者都是function性接口,可以在自Java8以来的Lambdaexpression式中使用。

ExecutorService接口中的方法是

 <T> Future<T> submit(Callable<T> task); Future<?> submit(Runnable task); <T> Future<T> submit(Runnable task, T result); 

Callable和Runnable之间的区别以及使用它们的用例在前面的答案中都很清楚。

我将提供有关exception处理部分的更多信息。

我写了一个代码来模拟算术exception:除以零

如果使用Executor.html#execute提交Runnable ,则会在处理Runnable任务期间返回Exceptions。

如果您提交了Callable,并且没有检查Future任务的状态(通过在Future上调用get()方法),将会误导未来任务的结果。

future.isDone()在Callable task call()方法中遇到的被零除exception的情况下也返回true

这些types的exception由Framework在内部捕获并隐藏。

看看示例代码(未在Future上检查get()方法)。

 import java.util.concurrent.*; import java.util.*; public class CallableDemo{ public CallableDemo(){ System.out.println("creating service"); ExecutorService service = Executors.newFixedThreadPool(10); List<MyCallable> futureList = new ArrayList<MyCallable>(); for ( int i=0; i<12; i++){ MyCallable myCallable = new MyCallable((long)i); futureList.add(myCallable); } System.out.println("Start"); try{ List<Future<Long>> futures = service.invokeAll(futureList); for(Future<Long> future : futures){ try{ System.out.println("future.isDone = " + future.isDone()); //System.out.println("future: call ="+future.get()); } catch(Exception err1){ err1.printStackTrace(); } } }catch(Exception err){ err.printStackTrace(); } service.shutdown(); } public static void main(String args[]){ CallableDemo demo = new CallableDemo(); } class MyCallable implements Callable<Long>{ Long id = 0L; public MyCallable(Long val){ this.id = val; } public Long call(){ int a=4, b = 0; System.out.println("a/b:"+(a/b)); return id; } } } 

输出:

 java CallableDemo creating service Start future.isDone = true future.isDone = true future.isDone = true future.isDone = true future.isDone = true future.isDone = true future.isDone = true future.isDone = true future.isDone = true future.isDone = true future.isDone = true future.isDone = true 

现在取消注释代码

 //System.out.println("future: call ="+future.get()); 

 System.out.println("future: call ="+future.get()); 

输出:

 java CallableDemo creating service Start future.isDone = true java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero at java.util.concurrent.FutureTask.report(FutureTask.java:122) at java.util.concurrent.FutureTask.get(FutureTask.java:188) at CallableDemo.<init>(CallableDemo.java:19) at CallableDemo.main(CallableDemo.java:27) Caused by: java.lang.ArithmeticException: / by zero at CallableDemo$MyCallable.call(CallableDemo.java:36) at CallableDemo$MyCallable.call(CallableDemo.java:29) at java.util.concurrent.FutureTask.run(FutureTask.java:262) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:744) 

处理这些types的例外的其他方法在下面解释:

处理来自Java ExecutorService任务的exception

为什么UncaughtExceptionHandler不能被ExecutorService调用?

正如前面已经提到的,Callable是一个相对较新的接口,它是作为并发包的一部分引入的。 Callable和Runnable都可以和执行程序一起使用。 类Thread(实现Runnable本身)只支持Runnable。

您仍然可以使用Runnable与执行程序。 Callable的好处是你可以把它发送给执行者,并立即取回将来的结果,当执行结束时会被更新。 Runnable也可以实现,但在这种情况下,您必须自己pipe理结果。 例如,您可以创build结果队列来保存所有结果。 其他线程可以在这个队列中等待并处理到达的结果。

 +-------------------------------------+--------------------------------------------------------------------------------------------------+ | Runnable | Callable<T> | +-------------------------------------+--------------------------------------------------------------------------------------------------+ | Introduced in Java 1.0 of java.lang | Introduced in Java 1.5 of java.util.concurrent library | | Runnable cannot be parametrized | Callable is a parametrized type whose type parameter indicates the return type of its run method | | Runnable has run() method | Callable has call() method | | Runnable.run() returns void | Callable.call() returns a value of Type T | | Can not throw Checked Exceptions | Can throw Checked Exceptions | +-------------------------------------+--------------------------------------------------------------------------------------------------+ 

Java的devise者觉得需要扩展Runnable接口的function,但是他们不想影响Runnable接口的使用,也许这就是为什么他们在Java 1.5中有一个名为Callable的独立接口的原因改变已经存在的Runnable接口,这个接口自Java 1.0以来已经是Java的一部分。 资源

Callable和Runnable之间的区别如下:

  1. Callable在JDK 5.0中引入,但Runnable在JDK 1.0中引入
  2. Callable具有call()方法,但Runnable具有run()方法。
  3. Callable具有返回值的调用方法,但Callable具有run方法不返回任何值。
  4. 调用方法可以抛出检查exception,但运行方法不能抛出检查exception。
  5. Callable使用submit()方法放入任务队列,但Runnable使用execute()方法放入任务队列。