Разница между выполняемыми и вызываемыми интерфейсами в Java

StackOverflow https://stackoverflow.com/questions/141284

Вопрос

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

Это было полезно?

Решение

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

Вызываемый интерфейс похож на 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() способ.

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

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

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

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

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

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

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

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

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

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

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

Callable интерфейс объявляет call() метод, и вам нужно предоставить обобщения в качестве типа объекта, который 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. A Runnable объект не возвращает результат, в то время как Callable объект возвращает результат.
  2. A Runnable объект не может выдать проверяемое исключение, когда Callable объект может выдавать исключение.
  3. Тот Самый Runnable интерфейс существует со времен Java 1.0, тогда как Callable был представлен только в Java 1.5.

Некоторые сходства включают в себя

  1. Экземпляры классов, реализующих выполняемые или вызываемые интерфейсы, потенциально могут быть выполнены другим потоком.
  2. Экземпляр как вызываемого, так и выполняемого интерфейсов может быть выполнен 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.

Вызываемый:Задача, которая возвращает результат и может выдавать исключение.Разработчики определяют единственный метод без аргументов, вызываемый call .Тот Самый Callable интерфейс похож на Runnable, в том смысле, что оба предназначены для классов, экземпляры которых потенциально выполняются другим потоком.A 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 может быть передан Вызвать все метод, отличающийся от Runnable.Методы invokeAny и invokeAll выполняйте наиболее часто используемые формы массового выполнения, выполняя набор задач, а затем ожидая завершения хотя бы одной или всех

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

Как уже упоминалось здесь, Callable - относительно новый интерфейс, и он был представлен как часть пакета concurrency.С исполнителями можно использовать как вызываемые, так и выполняемые.Класс 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. Источник

Различия между вызываемыми и выполняемыми заключаются в следующем:

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

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

Разница между Работоспособный и вызываемый как показано ниже--

1) The выполнить() способ получения работоспособный ВОЗВРАТ пустота, означает, что если вы хотите, чтобы ваш поток возвращал что-то, что вы можете использовать дальше, то у вас есть нет выбора с Runnable run() способ.Решение есть "Вызываемый", Если вы хотите вернуть какую - либо вещь в виде объект тогда ты следует использовать Callable вместо Runnable.Вызываемый интерфейс имеет метод 'call()', который возвращает объект.

Подпись метода - Работоспособный->

public void run(){}

Вызываемый ->

public Object call(){}

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

 public Object call() throws Exception {}

3) Работоспособный исходит из наследия java 1.0 версия, но вызываемый вошел Java 1.5 версия с Исполнитель рамки.

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

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

Выполняемый (vs) Вызываемый наступает момент, когда мы используем Executer framework.

ExecutorService - это подинтерфейс Executor, который принимает как выполняемые, так и вызываемые задачи.

Более ранняя многопоточность может быть достигнута с помощью интерфейса 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 который возвращает результат и может выдавать исключение.

  • Единый Абстрактный Метод :Как вызываемый, так и выполняемый интерфейс имеют единый абстрактный метод, что означает, что они могут использоваться в лямбда-выражениях в java 8.

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

Существует несколько различных способов делегирования задач для выполнения Служба исполнителя.

  • execute(Runnable task):void создает новый поток, но не блокирует основной поток или вызывающий поток, поскольку этот метод возвращает void.
  • submit(Callable<?>):Future<?>, submit(Runnable):Future<?> создает новый поток и блокирует основной поток при использовании future.get() будущее.get().

Пример использования интерфейсов, которые можно запускать, вызываемых с помощью Executor framework.

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();
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top