在Java中设计并发线程时,使用Runnable接口和Callable接口有什么区别,为什么你会选择其中一个而不是另一个?


当前回答

可调用接口声明call()方法,您需要提供泛型类型的对象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()方法,当你创建一个带有Runnable的线程并在其上调用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()方法执行。 两者都是功能接口,从Java8开始就可以在Lambda表达式中使用。

ExecutorService接口中的方法是

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

其他回答

可调用接口声明call()方法,您需要提供泛型类型的对象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()方法,当你创建一个带有Runnable的线程并在其上调用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()方法执行。 两者都是功能接口,从Java8开始就可以在Lambda表达式中使用。

ExecutorService接口中的方法是

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

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

Runnable和Callable都运行在与调用线程不同的线程上。但是Callable可以返回值,而Runnable不能。那么这到底适用于什么呢?

Runnable:如果你有一个火和忘记任务,那么使用Runnable。将代码放在Runnable中,当run()方法被调用时,就可以执行任务了。调用线程实际上并不关心您何时执行任务。

Callable:如果您试图从任务中检索值,则使用Callable。可调用本身是做不到的。你将需要一个Future,你将它包裹在你的Callable中,并获得关于Future的价值。get()。在这里,调用线程将被阻塞,直到Future返回结果,然后等待Callable的call()方法执行。

因此,考虑一个到目标类的接口,其中定义了Runnable和Callable包装方法。调用类会随机调用你的接口方法,不知道哪个是Runnable,哪个是Callable。Runnable方法将异步执行,直到Callable方法被调用。这里调用类的线程将阻塞,因为您正在从目标类中检索值。

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

Java功能接口

它是一种与函数式编程相匹配的接口命名约定

//Runnable
interface Runnable {
    void run();
}

//Action - throws exception
interface Action {
    void run() throws Exception;
}

//Consumer - consumes a value/values, throws exception
//BiConsumer, 
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
//BiFunction, Function3...
public interface Function1<T, R> {
    R apply(T t) throws Exception;
}

...

//Executor
public interface Executor {
    void execute(Runnable command);
}

【快速闭包命名】

+----------------------------------------+--------------------------------------------------------------------------------------------------+
|              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接口的使用,这可能就是为什么他们在Java 1.5中使用一个单独的名为Callable的接口,而不是改变已经存在的Runnable接口的原因,因为Runnable接口自Java 1.0以来一直是Java的一部分。源

Callable需要实现call()方法,而Runnable需要实现run()方法。 Callable可以返回值,但Runnable不能。 Callable可以抛出受控异常,但Runnable不能。 可调用对象可以与ExecutorService#invokeXXX(Collection<?扩展Callable<T>> tasks)方法,但Runnable不能。 可运行的{ 空运行(); } 可调用接口<V> { V call()抛出异常; }