Разница между интерфейсами Runnable и Callable в Java

В чем разница между использованием Runnable а также Callable интерфейсы при проектировании параллельного потока в Java, почему вы выбрали бы один из других?

16 ответов

Решение

Смотрите объяснение здесь.

Интерфейс Callable похож на Runnable в том, что оба предназначены для классов, экземпляры которых потенциально выполняются другим потоком. Runnable, однако, не возвращает результат и не может выдать проверенное исключение.

Каковы различия в приложениях Runnable а также Callable, Разница только с параметром возврата, присутствующим в Callable?

В основном да. Смотрите ответы на этот вопрос. И Javadoc для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() метод.

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

Я нашел это в другом блоге, который может объяснить немного больше этих различий:

Хотя оба интерфейса реализованы классами, которые хотят выполнять в другом потоке выполнения, между этими двумя интерфейсами есть несколько отличий:

  • Callable<V> экземпляр возвращает результат типа Vтогда как Runnable Экземпляр не
  • Callable<V> Экземпляр может выдавать проверенные исключения, тогда как Runnable экземпляр не может

Разработчики Java чувствовали необходимость расширения возможностей Runnable интерфейс, но они не хотели влиять на использование Runnable интерфейс и, вероятно, это была причина, почему они пошли для того, чтобы иметь отдельный интерфейс с именем Callable в Java 1.5, чем изменение уже существующего Runnable,

Давайте посмотрим, где можно использовать Runnable и Callable.

Runnable и Callable оба работают в другом потоке, чем в вызывающем потоке. Но Callable может вернуть значение, а Runnable не может. Так, где это действительно применяется.

Runnable: если у вас есть задание на запуск и забывание, используйте Runnable. Поместите свой код в Runnable, и когда вызывается метод run(), вы можете выполнить свою задачу. Вызывающему потоку действительно все равно, когда вы выполняете свою задачу.

Callable: если вы пытаетесь получить значение из задачи, используйте Callable. Теперь вызываемый сам по себе не сделает работу. Вам понадобится Future, который вы оберните вокруг своего Callable и получите свои значения в future.get (). Здесь вызывающий поток будет заблокирован до тех пор, пока Future не вернется с результатами, которые, в свою очередь, ожидают выполнения метода call() Callable.

Так что подумайте об интерфейсе с целевым классом, в котором определены как упакованные методы Runnable, так и Callable. Вызывающий класс будет случайным образом вызывать методы вашего интерфейса, не зная, какой из них Runnable, а какой - Callable. Методы Runnable будут выполняться асинхронно, пока не будет вызван метод Callable. Здесь поток вызывающего класса будет блокироваться, поскольку вы извлекаете значения из целевого класса.

ПРИМЕЧАНИЕ. Внутри целевого класса вы можете выполнять вызовы Callable и Runnable в одном потоке-исполнителе, что делает этот механизм похожим на очередь последовательной отправки. Поэтому до тех пор, пока вызывающая сторона вызывает ваши обернутые методы Runnable, вызывающий поток будет выполняться очень быстро без блокировки. Как только он вызывает Callable, обернутый в метод Future, он должен блокироваться, пока не будут выполнены все остальные элементы в очереди. Только тогда метод вернется со значениями. Это механизм синхронизации.

Callable интерфейс объявляет 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();
}

Подводя итог несколько заметных различий

  1. Runnable объект не возвращает результат, тогда как Callable объект возвращает результат.
  2. Runnable объект не может выдать проверенное исключение, если Callable Объект может выдать исключение.
  3. Runnable интерфейс существует с Java 1.0, тогда как Callable был введен только в Java 1.5.

Немного сходств включают

  1. Экземпляры классов, которые реализуют интерфейсы Runnable или Callable, потенциально выполняются другим потоком.
  2. Экземпляр обоих интерфейсов Callable и Runnable может быть выполнен ExecutorService с помощью метода submit().
  3. Оба являются функциональными интерфейсами и могут использоваться в лямбда-выражениях начиная с Java8.

Методы в интерфейсе ExecutorService являются

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

Назначение этих интерфейсов из документации Oracle:

Выполняемый интерфейс должен быть реализован любым классом, экземпляры которого предназначены для выполнения Thread, Класс должен определять метод без аргументов, называемый run,

Callable: задача, которая возвращает результат и может вызвать исключение. Реализаторы определяют один метод без аргументов, называемый call. Callable интерфейс похож на Runnableв том, что оба предназначены для классов, чьи экземпляры потенциально выполняются другим потоком. Runnableоднако не возвращает результат и не может выдать проверенное исключение.

Другие отличия:

  1. Вы можете пройти Runnable создать тему. Но вы не можете создать новую тему, передав 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, методы invokeAny а также invokeAll выполнять наиболее часто используемые формы массового выполнения, выполняя набор задач и ожидая завершения хотя бы одного или всех

  4. Тривиальная разница: имя метода для реализации => run() за Runnable а также call() за Callable,

Разница между Callable и Runnable заключается в следующем:

  1. Callable представлен в JDK 5.0, а Runnable представлен в JDK 1.0
  2. У Callable есть метод call(), но у Runnable есть метод run().
  3. У Callable есть метод call, который возвращает значение, но у Runnable есть метод run, который не возвращает никакого значения.
  4. вызывающий метод может генерировать проверенное исключение, но метод run не может генерировать проверенное исключение.
  5. Вызываемое использование метода submit() для помещения в очередь задач, но запускаемое использование метода execute() для помещения в очередь задач.

Как уже упоминалось, Callable - это относительно новый интерфейс, который был представлен как часть пакета для параллелизма. И Callable, и Runnable могут использоваться с исполнителями. Класс Thread (который реализует сам Runnable) поддерживает только Runnable.

Вы все еще можете использовать Runnable с исполнителями. Преимущество Callable в том, что вы можете отправить его исполнителю и сразу же получить результат в будущем, который будет обновлен после завершения выполнения. То же самое может быть реализовано с помощью Runnable, но в этом случае вам придется управлять результатами самостоятельно. Например, вы можете создать очередь результатов, которая будет содержать все результаты. Другой поток может ждать в этой очереди и иметь дело с результатами, которые прибывают.

+-------------------------------------+--------------------------------------------------------------------------------------------------+
|              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 с Java 1.0. источник

Callable и Runnable оба похожи друг на друга и могут использовать в реализации потока. В случае реализации Runnable вы должны реализовать метод run(), но в случае вызываемого вы должны реализовать метод call(), оба метода работают одинаково, но метод вызываемого вызова () обладает большей гибкостью. Между ними есть некоторые различия.

Разница между Runnable и callable, как показано ниже:

1) Метод run() для runnable возвращает void, значит, если вы хотите, чтобы ваш поток возвращал что-то, что вы можете использовать в дальнейшем, тогда у вас нет выбора с помощью метода Runnable run(). Существует решение "Callable". Если вы хотите вернуть какую-либо вещь в форме объекта, вам следует использовать Callable вместо Runnable. Вызываемый интерфейс имеет метод call(), который возвращает Object.

Подпись метода - Runnable->

public void run(){}

Callable->

public Object call(){}

2) В случае использования метода Runnable run(), если возникает какое-либо проверенное исключение, вам необходимо обработать блок try catch, но в случае вызова метода Callable () вы можете выбросить проверенное исключение, как показано ниже

 public Object call() throws Exception {}

3) Runnable поставляется с устаревшей версией java 1.0, но функция callable появилась в версии Java 1.5 с платформой Executer.

Если вы знакомы с Executers, вам следует использовать Callable вместо Runnable.

Надеюсь, вы понимаете.

Runnable (против) Callable вступает в силу, когда мы используем среду исполнения.

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.

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

Это своего рода соглашение об именах интерфейсов, которое соответствует функциональному программированию.

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

...

В дополнение ко всем остальным ответам:

Мы не можем передавать / использовать Callable в отдельный поток для выполнения, т.е. Callable можно использовать только в Executor Framework. Но Runnable может быть передан отдельному потоку для выполнения (new Thread(new CustomRunnable())), а также может использоваться в Executor Framework.

открытый интерфейс Runnable: интерфейс Runnable должен быть реализован любым классом, экземпляры которого предназначены для выполнения потоком. Класс должен определять метод без аргументов, который называется run. Этот интерфейс предназначен для предоставления общего протокола для объектов, которые хотят выполнять код, пока они активны. Например, Runnable реализуется классом Thread. Быть активным просто означает, что поток был запущен и еще не был остановлен.

Кроме того, Runnable предоставляет средства для того, чтобы класс был активным, не наследуя Thread. Класс, реализующий Runnable, может выполняться без создания подкласса Thread, создавая экземпляр экземпляра Thread и передавая себя в качестве цели. В большинстве случаев интерфейс Runnable следует использовать, если вы планируете переопределить только метод run(), а не другие методы Thread. Это важно, потому что классы не должны быть разделены на подклассы, если программист не намеревается изменить или улучшить фундаментальное поведение класса.

Начиная с: JDK1.0


открытый интерфейс Callable:

Задача, которая возвращает результат и может вызвать исключение. Реализаторы определяют один метод без аргументов, называемый call. Интерфейс Callable похож на Runnable в том, что оба предназначены для классов, экземпляры которых потенциально выполняются другим потоком. Runnable, однако, не возвращает результат и не может выдать проверенное исключение.

Класс Executors содержит служебные методы для преобразования из других распространенных форм в вызываемые классы.

Начиная с: JDK 1,5

Работающий интерфейс

  • Это часть пакета java.lang, начиная с Java 1.0.
  • Он не может генерировать проверенное исключение.
  • Он не может вернуть результат вычисления.
  • В работающем интерфейсе необходимо переопределить метод run() в Java.

Вызываемый интерфейс

  • Это часть пакета java.util.concurrent начиная с Java 1.5.

  • Он может выдать проверенное исключение.

  • Он может возвращать результат параллельной обработки задачи.

  • Чтобы использовать Callable, вам нужно переопределить call()

Другие вопросы по тегам