Answers:
Runnable
和的应用有什么区别Callable
?区别仅在于其中包含return参数Callable
吗?
基本上是。请参阅此问题的答案。和的javadocCallable
。
如果两者
Callable
都能做到,Runnable
那么有什么需要呢?
因为该Runnable
界面无法完成所有Callable
操作!
Runnable
自Java 1.0以来一直存在,但Callable
仅在Java 1.5中引入...来处理Runnable
不支持的用例。从理论上讲,Java团队可以更改该Runnable.run()
方法的签名,但是这将破坏与1.5版之前的代码的二进制兼容性,因此在将旧的Java代码迁移到更新的JVM时需要重新编码。那是很大的不。Java致力于向后兼容...这一直是Java商业计算的最大卖点之一。
而且,很明显,在某些用例中,任务不需要返回结果或抛出检查异常。对于这些用例,使用Runnable
比从方法中使用Callable<Void>
并返回哑数(null
)更为简洁call()
。
Runnable
出于向后兼容的原因,该建议(大部分)存在。但是,难道不存在不必要(或不需要)实现(或要求)Callable
接口(例如in ScheduledFuture<?> ScheduledExecutorService.schedule(Runnable command, long delay, TimeUnit unit)
)的情况吗?因此,即使历史没有强迫当前的结果,以语言维护两个接口是否没有好处?
Runnable
将被修改。的“样板” return null;
是一个微弱的论点。(至少,这是我的决定……在您可以忽略向后兼容性的假设情况下。)
Callable
需要实现call()
方法而Runnable
需要实现run()
的方法。Callable
可以返回值,但是aRunnable
不能。Callable
可以抛出检查异常,但Runnable
不能。一个Callable
可以与使用ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)
方法,但Runnable
不能。
public interface Runnable {
void run();
}
public interface Callable<V> {
V call() throws Exception;
}
让我们看看在哪里可以使用Runnable和Callable。
Runnable和Callable都在与调用线程不同的线程上运行。但是Callable可以返回值,而Runnable不能。因此,这在哪里真正适用。
可运行的:如果您遇到了火灾却忘记了任务,请使用可运行的。将代码放入Runnable中,并在调用run()方法时可以执行任务。当您执行任务时,调用线程实际上并不在乎。
可调用:如果您尝试从任务中检索值,请使用可调用。现在,仅靠自身可调用将无法完成任务。您将需要一个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
对象不能抛出checked异常wheras一个Callable
对象可以抛出异常。Runnable
接口自Java 1.0开始就存在,而Callable
仅在Java 1.5中引入。相似之处很少
ExecutorService接口中的方法是
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);
oracle文档中这些接口的用途:
可运行接口应该由其实例打算由NET执行的任何类实现Thread
。该类必须定义一个没有参数的方法,称为run
。
可调用的:返回结果并可能引发异常的任务。实现者定义一个没有参数的单一方法,称为call。该Callable
接口与相似Runnable
,因为两者都是针对其实例可能由另一个线程执行的类设计的。Runnable
但是,A 不返回结果,并且不能引发已检查的异常。
其他差异:
您可以通过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()
for Runnable
和call()
for Callable
。
正如这里已经提到的,Callable是一个相对较新的接口,它是作为并发包的一部分引入的。Callable和Runnable均可与执行程序一起使用。类线程(自身实现Runnable)仅支持Runnable。
您仍然可以将Runnable与执行程序一起使用。Callable的优点是您可以将其发送给执行者,并立即取回Future结果,该结果将在执行完成后进行更新。Runnable可以实现相同的功能,但是在这种情况下,您必须自己管理结果。例如,您可以创建将保存所有结果的结果队列。其他线程可以在此队列上等待并处理到达的结果。
Future
或通过添加捕获所有未预料到的异常的钩子来做出一些努力:docs.oracle.com/javase/6/docs/api/ java / lang /…
+-------------------------------------+--------------------------------------------------------------------------------------------------+
| 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的设计师感到有必要扩展Runnable
接口的功能,但是他们不想影响Runnable
接口的使用,这可能就是为什么他们选择Callable
在Java 1.5 中使用单独的接口而不是更改已经存在的接口的原因。Runnable
自Java 1.0以来一直是Java一部分的现有接口。资源
Callable和Runnable两者彼此相似,可用于实现线程。在实现Runnable的情况下,必须实现run()方法,而在callable的情况下,必须实现call()方法,这两种方法的工作方式相似,但callable的call()方法具有更大的灵活性。两者之间存在一些差异。
区别Runnable接口和可赎回的below--
1)运行() runnable方法返回void,这意味着如果您希望线程返回可以进一步使用的内容,那么您就无法选择Runnable的run()方法。有一个解决方案“ Callable”,如果您想以对象形式返回任何东西,则应该使用Callable而不是Runnable。可调用接口的方法'call()'返回Object。
方法签名-Runnable->
public void run(){}
可通话->
public Object call(){}
2)如果出现Runnable run()方法,如果出现任何检查的异常,则必须使用try catch块进行处理,但是,如果使用Callable call()方法,则可以按以下方式引发检查的异常
public Object call() throws Exception {}
3) Runnable来自旧版Java 1.0版本,但可调用来自带有Executer框架的Java 1.5版本。
如果您熟悉Executers那么你应该使用可赎回的,而不是Runnable接口。
希望你能理解。
可运行(vs)可调用当我们使用Executer框架时,就很重要。
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()时创建新线程并阻止主线程。在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 Throwable;
}
...