Різниця між інтерфейсами Runnable та Callable на Java


492

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


2
Для додаткової дискусії, прочитавши цю сторінку, див .
barfuin

Відповіді:


444

Дивіться пояснення тут .

Інтерфейс Callable схожий на Runnable, оскільки обидва розроблені для класів, екземпляри яких потенційно виконуються іншим потоком. Однак Runnable не повертає результату і не може кинути перевірений виняток.


269

Які відмінності у застосуванні 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()методу фіктивного ( ) .


9
Цікаво, звідки ви взяли цю історію. Це дуже корисно.
павук

4
@prash - основні факти можна знайти в старих підручниках. Як і перше видання Java в горішці.
Стівен С

4
(@prash - Також ... почавши використовувати Java в епоху Java 1.1.)
Stephen C

1
@StephenC Якщо я правильно прочитав вашу відповідь, ви припускаєте, що Runnableіснує (значною мірою) з причин відсталої сумісності. Але хіба не буває ситуацій, коли впроваджувати (або вимагати) Callableінтерфейс (наприклад, в ScheduledFuture<?> ScheduledExecutorService.schedule(Runnable command, long delay, TimeUnit unit)) це зайве або занадто дорого ? Тож чи не є користю у підтримці обох інтерфейсів у мові, навіть історія не змусила поточного результату?
макс

1
@max - Ну я це сказав, і я все ще з цим погоджуюся. Однак це друга причина. Але навіть так, я підозрюю, що Runnable це було б змінено, якби не було необхідності збереження сумісності. "Котельня" return null;- слабкий аргумент. (Принаймні, це було б моє рішення ... в гіпотетичному контексті, коли можна ігнорувати зворотну сумісність.)
Stephen C

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

17
ExecutorService.submit (Виконане завдання) також існує і дуже корисний
Яїр Кукієлка

Runnable також можна використовувати з ExecutorService наступними способами - 1) ExecutorService.execute (Runnable) 2) ExecutorService.submit (Runnable)
Azam Khan

2
Також є Executor.submit (Callable <T> завдання), але ви не можете викликатиВсе або визиватиAny зі збіркою колекції завдань Runnable <<? продовжує Callable <T>> завдання
nikli

36

Я знайшов це в іншому блозі, який може трохи більше пояснити ці відмінності :

Хоча обидва інтерфейси реалізовані класами, які бажають виконати в іншому потоці виконання, але між двома інтерфейсами є кілька відмінностей, які:

  • Callable<V>Примірник повертає результат типу V, в той час як Runnableекземпляр не робить.
  • Callable<V>Примірник може кинути перевірені виключення, в той час як Runnableекземпляр не може

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


27

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

Обидва, які можна виконувати та дзвонити, працюють на іншій нитці, ніж виклична нитка. Але Callable може повернути значення, а Runnable не може. Тож де це насправді стосується.

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

Callable : Якщо ви намагаєтеся отримати значення із завдання, використовуйте Callable. Тепер зателефонований самостійно не зробить роботу. Вам знадобиться майбутнє, яке ви обернете навколо Callable і отримаєте свої цінності на future.get (). Тут потік виклику буде заблокований, поки Майбутнє не повернеться з результатами, які в свою чергу чекають на виконання методу call () Callable.

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

ПРИМІТКА: Всередині вашого цільового класу ви можете здійснювати дзвінки на Callable та Runnable за допомогою одного виконавця потоку, що робить цей механізм подібним до черги чергової відправки. Отже, поки абонент зателефонує вашим обернутим методам Runnable, потік виклику буде виконуватись дуже швидко без блокування. Як тільки він викликає Callable, загорнутий у метод Future, йому доведеться блокувати, поки не будуть виконані всі інші елементи в черзі. Тільки тоді метод повернеться зі значеннями. Це механізм синхронізації.


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()метод, який викликається, коли ви створюєте 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.

Небагато подібності включає

  1. Екземпляри класів, які реалізують інтерфейси Runnable або Callable, потенційно виконуються іншим потоком.
  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);

13

Призначення цих інтерфейсів із документації на oracle:

Інтерфейс для запуску повинен бути реалізований будь-яким класом, екземпляри якого призначені для виконання Thread. Клас повинен визначати метод без викликів аргументів run.

Викликається : завдання, яке повертає результат і може кинути виняток. Виконавці визначають єдиний метод без аргументів, званих 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.


11

Як вже було сказано тут, Callable є відносно новим інтерфейсом, і він був представлений у складі пакету одночасності. Як виконавці, так і виконавці можуть використовуватися з виконавцями. Thread Class (що реалізує сам Runnable) підтримує лише Runnable.

Ви все ще можете використовувати Runnable разом із виконавцями. Перевага Callable в тому, що ви можете надіслати його виконавцю та негайно отримати майбутній результат, який буде оновлений після завершення виконання. Те саме може бути реалізовано і з 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 з Java 1.0. джерело


7

Різниця між дзвінкими, які дзвонять

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

Важливо підкреслити, що перевірено Виняток , а не RuntimeException
BertKing

5

Абодва, що дзвоняться та виконуються , схожі між собою та можуть використовуватись у впровадженні потоку. У разі реалізації Runnable ви повинні реалізувати метод run (), але у випадку виклику вам потрібно застосувати метод call () , обидва способи працюють аналогічно, але метод call () має більшу гнучкість. Між ними є деякі відмінності.

Різниця між Runnable і викликається , як below--

1) Run () метод працездатних повертає недійсних , кошти , якщо ви хочете , щоб ваш зворотний потік то , що ви можете використовувати в подальшому , то у вас немає іншого вибору , з Runnable Run () метод. Існує рішення "Callable". Якщо ви хочете повернути будь-яку річ у формі об'єкта, вам слід використовувати Callable замість Runnable . Інтерфейс, що викликається, має метод "call ()", який повертає "Об'єкт" .

Підпис методу - Runnable->

public void run(){}

Дзвінки->

public Object call(){}

2) У разі методу Runnable run (), якщо виникає будь-який перевірений виняток, вам потрібно обробляти блок "try catch" , але у випадку методу Callable call () ви можете кинути перевірений виняток, як показано нижче

 public Object call() throws Exception {}

3) Runnable походить від застарілої версії Java 1.0 , але дзвони прийшли у версії Java 1.5 із Frameworkter .

Якщо ви знайомі з виконавцями, вам слід використовувати Callable замість Runnable .

Сподіваюся, ви зрозуміли.


2

Виконувана (проти) Callable вступає в дію, коли ми використовуємо Executer Framework.

ExecutorService - це підрозділ інтерфейсу Executor, який приймає завдання як Runnable, так і Callable.

Раніше Multi-Threading можна досягти за допомогою інтерфейсу з 1.0 , але тут проблема полягає в тому, що після виконання потокового завдання ми не можемо зібрати інформацію про теми. Для збору даних ми можемо використовувати Статичні поля.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.

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

Існує кілька різних способів делегувати завдання для виконання в ExecutorService .

  • execute(Runnable task):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();
    }
}

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

...
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.