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 中使用。