Java中的Runnable和Callable接口的区别
在Java中devise并发线程时,使用Runnable和Callable接口有何区别,为什么要select一个呢?
在这里看到解释。
Callable接口与Runnable相似,都是为其实例可能由另一个线程执行的类devise的。 但是,Runnable不会返回结果,也不会抛出检查的exception。
Runnable
和Callable
的应用程序有什么不同? 与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(); }
总结几个显着的差异是
-
Runnable
对象不返回结果,而Callable
对象返回结果。 - 一个
Runnable
对象不能抛出一个检查exception,当一个Callable
对象可以抛出一个exception。 - 自Java 1.0以来,
Runnable
接口就已经存在,而Callable
仅在Java 1.5中引入。
几个相似之处包括
- 实现Runnable或Callable接口的类的实例可能由另一个线程执行。
- Callable和Runnable接口的实例可以由ExecutorService通过submit()方法执行。
- 两者都是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之间的区别如下:
- Callable在JDK 5.0中引入,但Runnable在JDK 1.0中引入
- Callable具有call()方法,但Runnable具有run()方法。
- Callable具有返回值的调用方法,但Callable具有run方法不返回任何值。
- 调用方法可以抛出检查exception,但运行方法不能抛出检查exception。
- Callable使用submit()方法放入任务队列,但Runnable使用execute()方法放入任务队列。