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


Answers:


444

请参阅此处的说明。

Callable接口与Runnable相似,两者均适用于其实例可能由另一个线程执行的类。但是,Runnable不会返回结果,也不能引发已检查的异常。


269

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


9
我不知道你从哪里得到这个历史的。这是非常有用的。
蜘蛛侠2014年

4
@prash-基本事实可在旧教科书中找到。就像第一版Java in Nutshell。
斯蒂芬·C

4
(@prash-也...通过在Java 1.1时代开始使用Java。)
Stephen C

1
@StephenC如果我正确地阅读了您的答案,则表示Runnable出于向后兼容的原因,该建议(大部分)存在。但是,难道不存在不必要(或不需要)实现(或要求)Callable接口(例如in ScheduledFuture<?> ScheduledExecutorService.schedule(Runnable command, long delay, TimeUnit unit))的情况吗?因此,即使历史没有强迫当前的结果,以语言维护两个接口是否没有好处?
最高

1
@max-我说过,但我仍然同意。但是,这是第二个原因。但是即使这样,我仍然怀疑如果没有必要保持兼容性的话,该内容Runnable 将被修改。的“样板” return null;是一个微弱的论点。(至少,这是我的决定……在您可以忽略向后兼容性的假设情况下。)
Stephen C

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

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

17
ExecutorService.submit(可运行任务)也存在并且非常有用
Yair Kukielka 2015年

Runnable也可以通过以下方式与ExecutorService一起使用:1)ExecutorService.execute(Runnable)2)ExecutorService.submit(Runnable)
Azam Khan

2
也有Executor.submit(Callable <T> task),但是您不能使用Runnable任务Collection <?的集合调用invokeAll或invokeAny。扩展Callable <T >>任务
nikli

36

我在另一个博客中找到了这个,可以进一步解释这些差异

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

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

Java的设计人员感到有必要扩展Runnable接口的功能,但是他们不想影响Runnable接口的使用,这可能就是为什么他们选择Callable在Java 1.5 中使用单独的接口而不是更改已经存在的接口的原因。现有的Runnable


27

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

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

可运行的:如果您遇到了火灾却忘记了任务,请使用可运行的。将代码放入Runnable中,并在调用run()方法时可以执行任务。当您执行任务时,调用线程实际上并不在乎。

可调用:如果您尝试从任务中检索值,请使用可调用。现在,仅靠自身可调用将无法完成任务。您将需要一个Future,将其包装在Callable上,并在future.get()上获取值。在这里,调用线程将被阻塞,直到Future返回结果,而结果又等待Callable的call()方法执行。

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

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


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()在创建具有可运行线程并在其上调用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对象不能抛出checked异常wheras一个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);

13

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

可运行接口应该由其实例打算由NET执行的任何类实现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();
        }
    
    }
  2. 使用Runnable消防和忘记调用。使用Callable来验证结果。

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

  4. 区别不大:要实现的方法名称=> run()for Runnablecall()for Callable


11

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

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


我想知道在Java中抛出线程异常的例子是什么?主线程将能够捕获该异常?如果没有,我不会使用Callable。亚历克斯,您对此有一些见识吗?谢谢!
万亿美元

1
与其他任何代码一样,在自定义线程中运行的代码也会引发异常。要在其他线程中捕获它,您必须使用自定义通知机制(例如,基于侦听器)或使用Future或通过添加捕获所有未预料到的异常的钩子来做出一些努力:docs.oracle.com/javase/6/docs/api/ java / lang /…
AlexR

很棒的信息!谢谢,亚历克斯!:)
万亿美元,

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

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                                                                     |
+-------------------------------------+--------------------------------------------------------------------------------------------------+

Java的设计师感到有必要扩展Runnable接口的功能,但是他们不想影响Runnable接口的使用,这可能就是为什么他们选择Callable在Java 1.5 中使用单独的接口而不是更改已经存在的接口的原因。Runnable自Java 1.0以来一直是Java一部分的现有接口。资源


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()方法放入任务队列。

重要的是要强调检查的Exception而不是RuntimeException
BertKing

5

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接口

希望你能理解。


2

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

0

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

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

...
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.