536

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

4

14 回答 14

478

请参阅此处的说明。

Callable 接口与 Runnable 类似,两者都是为实例可能由另一个线程执行的类设计的。但是,Runnable 不返回结果,也不能抛出检查异常。

于 2008-09-26T19:02:17.363 回答
295

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()

于 2010-10-17T06:44:49.880 回答
93
  • ACallable需要实现call()方法,而A 需要Runnable实现run()方法。
  • ACallable可以返回一个值,但 aRunnable不能。
  • ACallable可以抛出已检查的异常,但Runnable不能。
  • ACallable可以与ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)方法一起使用,但 aRunnable不能。

    public interface Runnable {
        void run();
    }
    
    public interface Callable<V> {
        V call() throws Exception;
    }
    
于 2013-05-20T13:34:41.853 回答
38

我在另一个博客中发现了这一点,可以更多地解释这些差异

虽然这两个接口都是由希望在不同的执行线程中执行的类实现的,但是这两个接口之间几乎没有区别,它们是:

  • Callable<V>实例返回 type 的结果,VRunnable实例不返回。
  • 实例可能会抛出已检查的Callable<V>异常,而Runnable实例不能

Java 的设计者觉得有必要扩展Runnable接口的功能,但他们不想影响Runnable接口的使用,这可能是他们选择Callable在 Java 1.5 中命名的单独接口而不是更改已经存在的接口的原因现有的Runnable.

于 2010-01-27T17:35:29.817 回答
33

让我们看看在哪里可以使用 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,它将不得不阻塞,直到所有其他排队的项目都被执行。只有这样,该方法才会返回值。这是一种同步机制。

于 2014-10-03T16:40:42.883 回答
18

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();
}

总结几个显着的区别是

  1. Runnable对象不返回结果,而对象Callable返回结果。
  2. Runnable对象不能抛出已检查异常,而对象Callable可以抛出异常。
  3. Runnable接口自 Java 1.0 以来一直存在,而Callable仅在 Java 1.5 中引入。

很少有相似之处包括

  1. 实现 Runnable 或 Callable 接口的类的实例可能由另一个线程执行。
  2. ExecutorService 可以通过 submit() 方法执行 Callable 和 Runnable 接口的实例。
  3. 两者都是函数式接口,并且可以在 Java8 之后的 Lambda 表达式中使用。

ExecutorService 接口中的方法是

<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);
于 2014-12-27T07:24:17.893 回答
15

oracle 文档中这些接口的用途:

Runnable接口应该由其实例打算由Thread. 该类必须定义一个没有参数的方法,称为run.

Callable:返回结果并可能引发异常的任务。实现者定义了一个没有参数的方法,称为 call。该Callable接口类似于Runnable,因为两者都是为实例可能由另一个线程执行的类设计的。但是, ARunnable不返回结果,也不能抛出检查异常。

其他区别:

  1. 您可以通过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();
        }
    
    }
    
  2. 用于Runnable火灾和忘记呼叫。用于Callable验证结果。

  3. Callable可以传递给invokeAll方法,不像Runnable. 方法invokeAnyinvokeAll执行最常用的批量执行形式,执行一组任务,然后等待至少一个或全部完成

  4. 微不足道的区别:要实现的方法名称 => run()forRunnablecall()for Callable

于 2016-02-15T10:59:17.890 回答
13

正如这里已经提到的,Callable 是一个相对较新的接口,它是作为并发包的一部分引入的。Callable 和 Runnable 都可以与执行程序一起使用。类 Thread(实现 Runnable 本身)仅支持 Runnable。

您仍然可以将 Runnable 与执行程序一起使用。Callable 的优点是您可以将其发送给 executor 并立即返回 Future 结果,该结果将在执行完成时更新。Runnable 也可以实现相同的功能,但在这种情况下,您必须自己管理结果。例如,您可以创建将保存所有结果的结果队列。其他线程可以在这个队列上等待并处理到达的结果。

于 2010-10-17T07:16:09.763 回答
11

Callable 和 Runnable 的区别如下:

  1. JDK 5.0 中引入了 Callable,但 JDK 1.0 中引入了 Runnable
  2. Callable 有 call() 方法,但 Runnable 有 run() 方法。
  3. Callable 具有返回值的 call 方法,但 Runnable 具有不返回任何值的 run 方法。
  4. call 方法可以抛出已检查的异常,但 run 方法不能抛出已检查的异常。
  5. Callable 使用 submit() 方法放入任务队列,而 Runnable 使用 execute() 方法放入任务队列。
于 2017-10-02T09:47:31.753 回答
10
+----------------------------------------+--------------------------------------------------------------------------------------------------+
|              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 一部分的现有接口。来源

于 2017-10-08T22:43:00.130 回答
8

Callable 和Runnable彼此相似,都可以用于实现线程。在实现Runnable的情况下,您必须实现run()方法,但在 callable 的情况下,您必须实现call()方法,两种方法的工作方式相似,但可调用的call()方法具有更大的灵活性。它们之间存在一些差异。

Runnablecallable之间的区别如下 -

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

希望你能理解。

于 2018-10-23T08:12:40.910 回答
5

当我们使用 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();
    }
}
于 2019-06-03T09:54:29.467 回答
3

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

//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);
}
于 2020-04-15T15:32:12.770 回答
0

除了所有其他答案:

我们不能将 Callable 传递/使用给单个线程执行,即 Callable 只能在 Executor Framework 中使用。但是,Runnable 可以传递给单个线程执行(new Thread(new CustomRunnable())),也可以在 Executor Framework 中使用。

于 2021-05-02T10:51:40.937 回答