Java中Runnable和Callable接口之间的区别

时间:2008-09-26 19:00:38

标签: java multithreading interface runnable callable

在Java中设计并发线程时使用RunnableCallable接口有什么区别,为什么要选择其中一个?

14 个答案:

答案 0 :(得分:407)

见解释here

  

Callable接口类似于   Runnable,两者都是设计的   对于其实例为的类   可能由另一个人执行   线。 但是,Runnable没有   返回一个结果,不能扔一个   检查异常。

答案 1 :(得分:245)

  

RunnableCallable的应用程序有何不同。仅与Callable中的返回参数有区别吗?

基本上,是的。查看this question的答案。还有javadoc for Callable

  

如果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())值。

答案 2 :(得分:77)

  • Callable需要实施call()方法,而Runnable需要实施run()方法。
  • Callable可以返回一个值,但Runnable不能返回。
  • Callable可以抛出已检查的异常,但Runnable不能。
  • Callable可以与ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)方法一起使用,但不能Runnable

    public interface Runnable {
        void run();
    }
    
    public interface Callable<V> {
        V call() throws Exception;
    }
    

答案 3 :(得分:36)

我在另一篇博客中发现了这一点,可以解释一下这些differences

虽然这两个接口都是由希望在不同执行线程中执行的类实现的,但两个接口之间的差异很小:

  • Callable<V>实例返回类型V的结果,而Runnable实例则不返回。
  • Callable<V>实例可能会抛出已检查的异常,而Runnable实例则无法

Java的设计者觉得需要扩展Runnable接口的功能,但他们不想影响Runnable接口的使用,这可能就是他们去的原因在Java 1.5中具有名为Callable的单独接口,而不是更改已存在的Runnable

答案 4 :(得分:26)

让我们看一下使用Runnable和Callable的位置。

Runnable和Callable都在与调用线程不同的线程上运行。但是Callable可以返回一个值,而Runnable则不能。那么这在哪里真的适用。

Runnable :如果您有一个fire and forget任务,那么请使用Runnable。将代码放在Runnable中,当调用run()方法时,您可以执行任务。执行任务时,调用线程实际上并不关心。

可调用:如果您尝试从任务中检索值,请使用Callable。现在可以自行调用不会起作用。你将需要一个围绕Callable的Future,并在future.get()上获取你的值。这里调用线程将被阻塞,直到Future返回结果,而结果又等待Callable的call()方法执行。

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

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

答案 5 :(得分:14)

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()方法的接口,当您使用runnable创建一个Thread并在其上调用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中。
  4. 几乎没有相似之处

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

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

答案 6 :(得分:14)

来自oracle文档的这些接口的用途:

Runnable接口应由任何其实例由Thread执行的类实现。该类必须定义一个名为run的无参数的方法。

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

其他差异:

  1. 您可以传递Runnable来创建Thread。但是你不能通过传递Callable作为参数来创建新的线程。您只能将Callable传递给ExecutorService个实例。

    Example:

    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不同,
  4. Runnable可以传递给invokeAll方法。方法invokeAnyinvokeAll执行最常用的批量执行形式,执行一组任务,然后等待至少一个或全部完成

  5. 微不足道的差异:要实施的方法名称=&gt; run()Runnablecall()Callable

答案 7 :(得分:11)

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

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

答案 8 :(得分:6)

+-------------------------------------+--------------------------------------------------------------------------------------------------+
|              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接口的使用,这可能就是他们去的原因用于在Java 1.5中具有名为Callable的单独接口,而不是更改已存在的Runnable接口,该接口自Java 1.0以来一直是Java的一部分。 source

答案 9 :(得分:5)

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. 可调用use()方法放入任务队列,但Runnable使用execute()方法放入任务队列。

答案 10 :(得分:2)

Callable和 Runnable 彼此相似,可用于实现线程。如果要实现 Runnable ,则必须实现 run()方法,但如果是可调用的,则必须实现 call()方法,这两种方法以类似的方式工作,但可调用的 call()方法具有更大的灵活性。两者之间存在一些差异。

可运行可调用之间的区别如下-

1)可运行 run()方法返回 void ,这意味着如果您希望线程返回一些可以进一步使用的东西,您无法选择Runnable run()方法。有一个解决方案'Callable',如果您想以 object 的形式返回任何东西,则应使用Callable而不是Runnable 。可调用接口具有方法'call()',该方法返回Object

方法签名- 可运行的->

public void run(){}

Callable->

public Object call(){}

2)如果使用 Runnable run()方法,如果出现任何已检查的异常,则必须使用try catch块进行处理,但是如果使用可以引发以下检查异常的可调用call()方法,如下所示

 public Object call() throws Exception {}

3)可运行来自旧版 java 1.0 版本,而可调用来自 Java 1.5 版本,其中< strong>执行者框架。

如果您熟悉执行者,则应该使用Callable而不是Runnable

希望您能理解

答案 11 :(得分:1)

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

//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;
}

...

答案 12 :(得分:0)

当使用Executer框架时,

可运行(vs)可调用就应运而生。

ExecutorService是Executor的子接口,它同时接受Runnable和Callable任务。

可以使用接口 Runnable 从1.0起 实现较早的多线程,但是这里的问题是完成线程后任务,我们无法收集线程信息。为了收集数据,我们可以使用“静态”字段。

示例分别收集每个学生数据的线程。

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

为解决此问题,他们引入了 Callable<V> 从1.5开始的 ,它返回结果并可能引发异常。

  • 单个抽象方法: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()时阻止主线程。

使用可运行,可与执行器框架一起调用的接口的示例。

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

答案 13 :(得分:0)

除了所有其他答案:

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