Runnable
在 Java 中设计并发线程时使用和接口有什么区别Callable
,为什么要选择一个而不是另一个?
14 回答
请参阅此处的说明。
Callable 接口与 Runnable 类似,两者都是为实例可能由另一个线程执行的类设计的。但是,Runnable 不返回结果,也不能抛出检查异常。
Runnable
和的应用有什么区别Callable
。差异仅与中存在的返回参数不同Callable
吗?
基本上,是的。请参阅此问题的答案。和用于Callable
.
如果
Callable
可以做到所有这些,那么需要两者兼有Runnable
吗?
因为Runnable
界面不能做所有事情Callable
!
Runnable
自 Java 1.0 以来一直存在,但Callable
仅在 Java 1.5 中引入......用于处理Runnable
不支持的用例。理论上,Java 团队可以更改Runnable.run()
方法的签名,但这会破坏与 1.5 之前代码的二进制兼容性,在将旧 Java 代码迁移到新 JVM 时需要重新编码。这是一个很大的禁忌。Java 力求向后兼容……这是 Java 在商业计算方面的最大卖点之一。
而且,显然,有些用例不需要任务返回结果或抛出检查异常。对于这些用例, usingRunnable
比使用Callable<Void>
并从方法返回一个虚拟 ( null
) 值更简洁call()
。
- A
Callable
需要实现call()
方法,而A 需要Runnable
实现run()
方法。 - A
Callable
可以返回一个值,但 aRunnable
不能。 - A
Callable
可以抛出已检查的异常,但Runnable
不能。 A
Callable
可以与ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)
方法一起使用,但 aRunnable
不能。public interface Runnable { void run(); } public interface Callable<V> { V call() throws Exception; }
我在另一个博客中发现了这一点,可以更多地解释这些差异:
虽然这两个接口都是由希望在不同的执行线程中执行的类实现的,但是这两个接口之间几乎没有区别,它们是:
Callable<V>
实例返回 type 的结果,V
而Runnable
实例不返回。- 实例可能会抛出已检查的
Callable<V>
异常,而Runnable
实例不能
Java 的设计者觉得有必要扩展Runnable
接口的功能,但他们不想影响Runnable
接口的使用,这可能是他们选择Callable
在 Java 1.5 中命名的单独接口而不是更改已经存在的接口的原因现有的Runnable
.
让我们看看在哪里可以使用 Runnable 和 Callable。
Runnable 和 Callable 都在与调用线程不同的线程上运行。但是 Callable 可以返回一个值,而 Runnable 不能。那么这在哪里真正适用。
Runnable:如果你有一个火而忘记的任务,那么使用 Runnable。将您的代码放在 Runnable 中,当调用 run() 方法时,您可以执行您的任务。当您执行任务时,调用线程真的不在乎。
Callable:如果您尝试从任务中检索值,请使用 Callable。现在可单独调用将无法完成这项工作。您将需要一个 Future 来包装您的 Callable 并在 future.get() 上获取您的值。这里调用线程将被阻塞,直到 Future 返回结果,结果又等待 Callable 的 call() 方法执行。
所以考虑一个目标类的接口,你定义了 Runnable 和 Callable 包装的方法。调用类会随机调用你的接口方法,不知道哪个是可运行的,哪个是可调用的。Runnable 方法将异步执行,直到调用 Callable 方法。这里调用类的线程将阻塞,因为您正在从目标类中检索值。
注意:在您的目标类中,您可以在单个线程执行程序上调用 Callable 和 Runnable,使这种机制类似于串行调度队列。因此,只要调用者调用您的 Runnable 包装方法,调用线程就会非常快地执行而不会阻塞。一旦它调用包装在 Future 方法中的 Callable,它将不得不阻塞,直到所有其他排队的项目都被执行。只有这样,该方法才会返回值。这是一种同步机制。
Callable
接口声明call()
方法,您需要提供泛型,因为 Object call() 的类型应该返回 -
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()
方法的接口,当您使用可运行对象创建线程并在其上调用 start() 时调用该方法。您也可以直接调用 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
对象不能抛出已检查异常,而对象Callable
可以抛出异常。- 该
Runnable
接口自 Java 1.0 以来一直存在,而Callable
仅在 Java 1.5 中引入。
很少有相似之处包括
- 实现 Runnable 或 Callable 接口的类的实例可能由另一个线程执行。
- ExecutorService 可以通过 submit() 方法执行 Callable 和 Runnable 接口的实例。
- 两者都是函数式接口,并且可以在 Java8 之后的 Lambda 表达式中使用。
ExecutorService 接口中的方法是
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);
oracle 文档中这些接口的用途:
Runnable接口应该由其实例打算由Thread
. 该类必须定义一个没有参数的方法,称为run
.
Callable:返回结果并可能引发异常的任务。实现者定义了一个没有参数的方法,称为 call。该Callable
接口类似于Runnable
,因为两者都是为实例可能由另一个线程执行的类设计的。但是, ARunnable
不返回结果,也不能抛出检查异常。
其他区别:
您可以通过
Runnable
创建一个Thread。但是您不能通过Callable
作为参数传递来创建新线程。您只能将 Callable 传递给ExecutorService
实例。public class HelloRunnable implements Runnable { public void run() { System.out.println("Hello from a thread!"); } public static void main(String args[]) { (new Thread(new HelloRunnable())).start(); } }
用于
Runnable
火灾和忘记呼叫。用于Callable
验证结果。Callable
可以传递给invokeAll方法,不像Runnable
. 方法invokeAny
和invokeAll
执行最常用的批量执行形式,执行一组任务,然后等待至少一个或全部完成微不足道的区别:要实现的方法名称 =>
run()
forRunnable
和call()
forCallable
。
正如这里已经提到的,Callable 是一个相对较新的接口,它是作为并发包的一部分引入的。Callable 和 Runnable 都可以与执行程序一起使用。类 Thread(实现 Runnable 本身)仅支持 Runnable。
您仍然可以将 Runnable 与执行程序一起使用。Callable 的优点是您可以将其发送给 executor 并立即返回 Future 结果,该结果将在执行完成时更新。Runnable 也可以实现相同的功能,但在这种情况下,您必须自己管理结果。例如,您可以创建将保存所有结果的结果队列。其他线程可以在这个队列上等待并处理到达的结果。
Callable 和 Runnable 的区别如下:
- JDK 5.0 中引入了 Callable,但 JDK 1.0 中引入了 Runnable
- Callable 有 call() 方法,但 Runnable 有 run() 方法。
- Callable 具有返回值的 call 方法,但 Runnable 具有不返回任何值的 run 方法。
- call 方法可以抛出已检查的异常,但 run 方法不能抛出已检查的异常。
- Callable 使用 submit() 方法放入任务队列,而 Runnable 使用 execute() 方法放入任务队列。
+----------------------------------------+--------------------------------------------------------------------------------------------------+
| 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 generic value V |
| No way to propagate checked exceptions | Callable's call()“throws Exception” clause so we can easily propagate checked exceptions further | |
+----------------------------------------+--------------------------------------------------------------------------------------------------+
Java 的设计者觉得有必要扩展Runnable
接口的功能,但他们不想影响Runnable
接口的使用,这可能是他们选择Callable
在 Java 1.5 中命名的单独接口而不是更改已经存在的接口的原因Runnable
自 Java 1.0 以来一直是 Java 一部分的现有接口。来源
Callable 和Runnable彼此相似,都可以用于实现线程。在实现Runnable的情况下,您必须实现run()方法,但在 callable 的情况下,您必须实现call()方法,两种方法的工作方式相似,但可调用的call()方法具有更大的灵活性。它们之间存在一些差异。
Runnable和callable之间的区别如下 -
1) runnable的run()方法返回void,这意味着如果您希望线程返回可以进一步使用的东西,那么您别无选择 Runnable run()方法。有一个解决方案'Callable',如果你想以对象的形式返回任何东西,那么你应该使用 Callable 而不是 Runnable。可调用接口具有返回 Object 的方法 'call()'。
方法签名 - Runnable->
public void run(){}
可调用->
public Object call(){}
2) 在Runnable run()方法的情况下,如果出现任何已检查的异常,则必须使用 try catch 块进行处理,但在Callable call()方法的情况下,您可以抛出已检查的异常,如下所示
public Object call() throws Exception {}
3) Runnable来自传统的java 1.0版本,但callable来自带有Executer框架的Java 1.5版本。
如果您熟悉Executers ,那么您应该使用 Callable 而不是 Runnable。
希望你能理解。
当我们使用 Executer 框架时,Runnable (vs) Callable就很重要了。
ExecutorService 是 的子接口Executor
,它接受 Runnable 和 Callable 任务。
从 1.0开始使用 Interface 可以实现早期的多线程,但是这里的问题是在完成线程任务后我们无法收集线程信息。为了收集数据,我们可能会使用静态字段。Runnable
示例 单独的线程来收集每个学生的数据。
static HashMap<String, List> multiTasksData = new HashMap();
public static void main(String[] args) {
Thread t1 = new Thread( new RunnableImpl(1), "T1" );
Thread t2 = new Thread( new RunnableImpl(2), "T2" );
Thread t3 = new Thread( new RunnableImpl(3), "T3" );
multiTasksData.put("T1", new ArrayList() ); // later get the value and update it.
multiTasksData.put("T2", new ArrayList() );
multiTasksData.put("T3", new ArrayList() );
}
为了解决这个问题,他们从 1.5开始引入,它返回一个结果并可能引发异常。Callable<V>
单一抽象方法:Callable 和 Runnable 接口都有一个抽象方法,这意味着它们可以在 java 8 的 lambda 表达式中使用。
public interface Runnable { public void run(); } public interface Callable<Object> { public Object call() throws Exception; }
有几种不同的方法可以将要执行的任务委托给ExecutorService。
execute(Runnable task):void
板条箱新线程但不阻塞主线程或调用者线程,因为此方法返回 void。submit(Callable<?>):Future<?>
,submit(Runnable):Future<?>
在使用future.get()时创建新线程并阻塞主线程。
将接口 Runnable、Callable 与 Executor 框架一起使用的示例。
class CallableTask implements Callable<Integer> {
private int num = 0;
public CallableTask(int num) {
this.num = num;
}
@Override
public Integer call() throws Exception {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " : Started Task...");
for (int i = 0; i < 5; i++) {
System.out.println(i + " : " + threadName + " : " + num);
num = num + i;
MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
}
System.out.println(threadName + " : Completed Task. Final Value : "+ num);
return num;
}
}
class RunnableTask implements Runnable {
private int num = 0;
public RunnableTask(int num) {
this.num = num;
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " : Started Task...");
for (int i = 0; i < 5; i++) {
System.out.println(i + " : " + threadName + " : " + num);
num = num + i;
MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
}
System.out.println(threadName + " : Completed Task. Final Value : "+ num);
}
}
public class MainThread_Wait_TillWorkerThreadsComplete {
public static void main(String[] args) throws InterruptedException, ExecutionException {
System.out.println("Main Thread start...");
Instant start = java.time.Instant.now();
runnableThreads();
callableThreads();
Instant end = java.time.Instant.now();
Duration between = java.time.Duration.between(start, end);
System.out.format("Time taken : %02d:%02d.%04d \n", between.toMinutes(), between.getSeconds(), between.toMillis());
System.out.println("Main Thread completed...");
}
public static void runnableThreads() throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newFixedThreadPool(4);
Future<?> f1 = executor.submit( new RunnableTask(5) );
Future<?> f2 = executor.submit( new RunnableTask(2) );
Future<?> f3 = executor.submit( new RunnableTask(1) );
// Waits until pool-thread complete, return null upon successful completion.
System.out.println("F1 : "+ f1.get());
System.out.println("F2 : "+ f2.get());
System.out.println("F3 : "+ f3.get());
executor.shutdown();
}
public static void callableThreads() throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newFixedThreadPool(4);
Future<Integer> f1 = executor.submit( new CallableTask(5) );
Future<Integer> f2 = executor.submit( new CallableTask(2) );
Future<Integer> f3 = executor.submit( new CallableTask(1) );
// Waits until pool-thread complete, returns the result.
System.out.println("F1 : "+ f1.get());
System.out.println("F2 : "+ f2.get());
System.out.println("F3 : "+ f3.get());
executor.shutdown();
}
}
它是一种与函数式编程相匹配的接口命名约定
//Runnable
interface Runnable {
void run();
}
//Action - throws exception
interface Action {
void run() throws Exception;
}
//Consumer - consumes a value/values, throws exception
interface Consumer1<T> {
void accept(T t) throws Exception;
}
//Callable - return result, throws exception
interface Callable<R> {
R call() throws Exception;
}
//Supplier - returns result, throws exception
interface Supplier<R> {
R get() throws Exception;
}
//Predicate - consumes a value/values, returns true or false, throws exception
interface Predicate1<T> {
boolean test(T t) throws Exception;
}
//Function - consumes a value/values, returns result, throws exception
public interface Function1<T, R> {
R apply(T t) throws Exception;
}
...
//Executor
public interface Executor {
void execute(Runnable command);
}
除了所有其他答案:
我们不能将 Callable 传递/使用给单个线程执行,即 Callable 只能在 Executor Framework 中使用。但是,Runnable 可以传递给单个线程执行(new Thread(new CustomRunnable())),也可以在 Executor Framework 中使用。