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

Sco*_*ttm 473 java multithreading interface callable runnable

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

Jor*_*ira 427

请看这里的解释.

Callable接口类似于Runnable,因为它们都是为其实例可能由另一个线程执行的类而设计的.但是,Runnable不会返回结果,也不会抛出已检查的异常.

  • 确切地。此外,Runnable 任务可以使用 Thread 类或 ExecutorService 来运行,而 Callables 只能使用后者来运行。 (6认同)

Ste*_*n C 261

Runnable和和的应用有什么不同Callable.区别仅在于返回参数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>和返回方法中的dummy(null)值更简洁call().

  • 我想知道你从哪里得到这段历史.这非常有用. (8认同)
  • @prash - 基本事实可以在旧教科书中找到.就像果壳中的第一版Java一样. (4认同)
  • (@prash - 也是......在Java 1.1时代开始使用Java.) (4认同)

nik*_*kli 78

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

    public interface Runnable {
        void run();
    }
    
    public interface Callable<V> {
        V call() throws Exception;
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • ExecutorService.submit(Runnable task)也存在并且非常有用 (17认同)
  • 也有Executor.submit(Callable &lt;T&gt;任务),但是您不能调用带有Runnable任务Collection &lt;?的collection的invokeAll或invokeAny?扩展Callable &lt;T &gt;&gt;任务 (2认同)

amo*_*ran 36

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

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

  • 一个Callable<V>实例返回类型的结果V,而一个Runnable实例不会.
  • 一个Callable<V>实例可能抛出检查异常,而Runnable实例不能

Java的设计者觉得需要扩展Runnable接口的功能,但他们不想影响Runnable接口的使用,这可能就是为什么他们选择Callable在Java 1.5中使用一个单独的接口而不是改变已经接口的原因.现有的Runnable.


Kri*_*ian 27

让我们看一下使用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方法中,它就必须阻塞,直到所有其他排队的项都被执行.只有这样,该方法才会返回值.这是一种同步机制.


Ani*_*kur 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;
}
Run Code Online (Sandbox Code Playgroud)

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();
}
Run Code Online (Sandbox Code Playgroud)

总结一些值得注意的差异是

  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);
Run Code Online (Sandbox Code Playgroud)


Rav*_*abu 13

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

Runnable接口应由任何其实例要由a执行的类实现Thread.该类必须定义一个不带参数的方法run.

可调用:返回结果并可能抛出异常的任务.实现者定义一个没有名为call的参数的方法.该Callable接口类似于Runnable,在这两个被设计用于一个其实例潜在地由另一个线程执行.Runnable但是,A 不会返回结果,也不能抛出已检查的异常.

其他差异:

  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();
        }
    
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用Runnable消防和忘记调用.使用Callable来验证结果.

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

  4. 微不足道的差异:要实现的方法名称=> run()for Runnablecall()for Callable.


Ale*_*exR 11

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

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

  • 我赞成这个答案,因为它断言(如果按面值考虑是正确的)必须将线程池模型与可调用对象一起使用。显然不幸的是,人们无法扩展“Thread”以有意义地使用“Callable”接口,以便可以自定义单个线程来执行可调用的事情和开发人员可能想要的其他事情。如果任何阅读此评论的人认为我错了,我想知道得更好... (2认同)

Pre*_*raj 8

+-------------------------------------+--------------------------------------------------------------------------------------------------+
|              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                                                                     |
+-------------------------------------+--------------------------------------------------------------------------------------------------+
Run Code Online (Sandbox Code Playgroud)

Java的设计者觉得需要扩展Runnable接口的功能,但他们不想影响Runnable接口的使用,这可能就是为什么他们选择Callable在Java 1.5中使用一个单独的接口而不是改变已经接口的原因.现有的Runnable接口,自Java 1.0以来一直是Java的一部分.资源


Sud*_*dey 8

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

Runnablecallable之间的区别如下-

1) runnablerun()方法返回void,这意味着如果你想让你的线程返回一些你可以进一步使用的东西,那么你别无选择 Runnable run()方法。有一个解决方案'Callable',如果你想以对象的形式返回任何东西,那么你应该使用 Callable 而不是 Runnable。可调用接口具有返回 Object 的方法“call()”

方法签名 - Runnable->

public void run(){}
Run Code Online (Sandbox Code Playgroud)

可调用->

public Object call(){}
Run Code Online (Sandbox Code Playgroud)

2) 在Runnable run()方法的情况下,如果出现任何检查异常,那么您必须使用 try catch 块处理,但在Callable call()方法的情况下,您可以抛出检查异常,如下所示

 public Object call() throws Exception {}
Run Code Online (Sandbox Code Playgroud)

3) Runnable来自遗留的java 1.0版本,但callable来自带有Executer框架的Java 1.5版本。

如果您熟悉Executers,那么您应该使用 Callable 而不是 Runnable

希望你能理解。


Ram*_*pta 7

Callable和Runnable之间的区别如下:

  1. JDK 5.0中引入了Callable,而JDK 1.0中引入了Runnable。
  2. Callable具有call()方法,而Runnable具有run()方法。
  3. Callable具有可以返回值的call方法,而Runnable具有可以不返回任何值的run方法。
  4. 调用方法可以引发检查异常,但运行方法不能引发检查异常。
  5. 可调用的使用commit()方法放入任务队列,而Runnable的使用execute()方法放入任务队列。


Yas*_*ash 5

当我们使用 Executer 框架时,Runnable (vs) Callable就显得尤为重要。

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

早先的Multi-Threading可以使用Interface 1.0以来的方式来实现,但是这里的问题是在完成线程任务后我们无法收集到Threads信息。为了收集数据,我们可能会使用静态字段。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() );
}
Run Code Online (Sandbox Code Playgroud)

为了解决这个问题,他们引入了自 1.5 以来,它返回一个结果并可能抛出异常。Callable<V>

  • 单一抽象方法:Callable 和 Runnable 接口都有一个抽象方法,这意味着它们可以在 java 8 中的 lambda 表达式中使用。

    public interface Runnable {
    public void run();
    }
    
    public interface Callable<Object> {
        public Object call() throws Exception;
    }
    
    Run Code Online (Sandbox Code Playgroud)

有几种不同的方法可以将要执行的任务委托给ExecutorService

  • execute(Runnable task):void crates 新线程但不阻塞主线程或调用者线程,因为此方法返回 void。
  • submit(Callable<?>):Future<?>,submit(Runnable):Future<?>在使用future.get()时创建新线程并阻塞主线程。

使用接口 Runnable、Callable with 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();
    }
}
Run Code Online (Sandbox Code Playgroud)