Różnica między interfejsami Runnable i Callable w Javie


Odpowiedzi:


444

Zobacz wyjaśnienie tutaj .

Interfejs wywoływalny jest podobny do Runnable, ponieważ oba są przeznaczone dla klas, których instancje są potencjalnie wykonywane przez inny wątek. Runnable jednak nie zwraca wyniku i nie może zgłosić sprawdzonego wyjątku.


269

Jakie są różnice w aplikacjach Runnablei Callable. Czy różnica jest tylko w przypadku obecności parametru powrotu Callable?

Zasadniczo tak. Zobacz odpowiedzi na to pytanie . I javadoc dlaCallable .

Jaka jest potrzeba posiadania obu, jeśli Callablemoże zrobić wszystko, co Runnablerobi?

Ponieważ Runnableinterfejs nie może zrobić wszystkiego, co Callablerobi!

Runnableistnieje już od wersji Java 1.0, ale Callablezostał wprowadzony tylko w wersji Java 1.5 ... do obsługi przypadków użycia, które Runnablenie obsługują. Teoretycznie zespół Java mógł zmienić sygnaturę Runnable.run()metody, ale złamałoby to binarną kompatybilność z kodem wcześniejszym niż 1.5, wymagając ponownego przekodowania podczas migracji starego kodu Java do nowszych JVM. To DUŻY NIE-NIE. Java stara się być kompatybilna wstecz ... i to był jeden z największych punktów sprzedaży Java dla komputerów biznesowych.

Oczywiście są przypadki użycia, w których zadanie nie musi zwracać wyniku ani zgłaszać sprawdzonego wyjątku. W przypadku tych przypadków użycie Runnablejest bardziej zwięzłe niż użycie Callable<Void>i zwrócenie wartości dummy ( null) z call()metody.


9
Zastanawiam się, skąd masz tę historię. To jest bardzo przydatne.
Spider

4
@prash - podstawowe fakty można znaleźć w starych podręcznikach. Podobnie jak pierwsza edycja Java w pigułce.
Stephen C

4
(@prash - Także ... zaczynając używać Java w erze Java 1.1.)
Stephen C

1
@StephenC Jeśli poprawnie przeczytałem twoją odpowiedź, sugerujesz, że Runnableistnieje (w dużej mierze) ze względu na kompatybilność wsteczną. Ale czy nie są sytuacje, w których wdrożenie (lub wymaganie) Callableinterfejsu (np. W ScheduledFuture<?> ScheduledExecutorService.schedule(Runnable command, long delay, TimeUnit unit)) jest niepotrzebne lub zbyt drogie ? Czy zatem utrzymanie obu interfejsów w języku nie ma żadnej korzyści, nawet historia nie wymusiła obecnego wyniku?
maksymalnie

1
@max - Cóż, powiedziałem to i nadal się z tym zgadzam. Jest to jednak drugi powód. Ale mimo to podejrzewam, że Runnable zostałby zmodyfikowany, gdyby nie było konieczności zachowania zgodności. „Płyta kotła” return null;jest słabym argumentem. (Przynajmniej taka byłaby moja decyzja ... w hipotetycznym kontekście, w którym można zignorować wsteczną kompatybilność.)
Stephen C

81
  • CallableMusi wdrożyć call()metody natomiast Runnablemusi wdrożyć run()metody.
  • A Callablemoże zwrócić wartość, ale Runnablenie może.
  • CallableMoże rzucić wyjątek sprawdzone ale Runnablenie mogę.
  • CallableMoże być używany z ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)metod, ale Runnablenie może być.

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

17
ExecutorService.submit (zadanie Runnable) również istnieje i jest bardzo przydatne
Yair Kukielka

Runnable może być również używany z ExecutorService w następujący sposób: 1) ExecutorService.execute (Runnable) 2) ExecutorService.submit (Runnable)
Azam Khan

2
Istnieje również plik Executor.submit (zadanie na żądanie <T>), ale nie można wywoływać wszystkiego ani wywoływać dowolnego z kolekcją zadań Runnable Collection <? rozszerza zadania
wywoływalne

36

Znalazłem to na innym blogu, który może wyjaśnić nieco więcej tych różnic :

Chociaż oba interfejsy są implementowane przez klasy, które chcą wykonać w innym wątku wykonania, ale istnieje kilka różnic między tymi dwoma interfejsami, które są:

  • Callable<V>Instancji zwraca wynik typu V, podczas gdy Runnableinstancja nie.
  • Callable<V>Instancja może rzucać sprawdzonych wyjątków, natomiast Runnableinstancji nie może

Projektanci Java odczuli potrzebę rozszerzenia możliwości Runnableinterfejsu, ale nie chcieli wpływać na wykorzystanie Runnableinterfejsu i prawdopodobnie dlatego zdecydowali się na osobny interfejs o nazwie CallableJava 1.5, niż zmieniający już istniejących Runnable.


27

Przyjrzyjmy się, gdzie można użyć Runnable i Callable.

Zarówno Runnable, jak i Callable działają na innym wątku niż wątek wywołujący. Ale Callable może zwrócić wartość, a Runnable nie. Gdzie to naprawdę ma zastosowanie.

Runnable : Jeśli masz zadanie pożaru i zapomnienia, użyj Runnable. Umieść swój kod w Runnable, a po wywołaniu metody run () możesz wykonać swoje zadanie. Wątek wywołujący naprawdę nie ma znaczenia podczas wykonywania zadania.

Callable : Jeśli próbujesz odzyskać wartość z zadania, użyj Callable. Teraz samodzielne wywołanie nie zadziała. Będziesz potrzebować Przyszłości, którą obejmiesz na żądanie i uzyskasz wartości na future.get (). Tutaj wątek wywołujący zostanie zablokowany, aż Przyszłość powróci z wynikami, które z kolei czekają na wykonanie metody call () Callable.

Pomyśl więc o interfejsie do klasy docelowej, w którym zdefiniowano zarówno metody zawijalne Runnable, jak i Callable. Klasa wywołująca losowo wywołuje metody interfejsu, nie wiedząc, która jest Runnable, a która Callable. Metody Runnable będą wykonywane asynchronicznie, dopóki nie zostanie wywołana metoda Callable. Tutaj wątek klasy wywołującej zostanie zablokowany, ponieważ pobierasz wartości z klasy docelowej.

UWAGA: W klasie docelowej można wykonywać wywołania do wywoływania i uruchamiania na jednym executorze wątków, co czyni ten mechanizm podobny do szeregowej kolejki wysyłania. Tak długo, jak wywołujący wywoła metody opakowane w Runnable, wątek wywołujący będzie działał naprawdę szybko bez blokowania. Gdy tylko wywoła metodę Callable zawiniętą w metodę Future, będzie musiała blokować do momentu wykonania wszystkich pozostałych pozycji w kolejce. Dopiero wtedy metoda zwróci wartości. To jest mechanizm synchronizacji.


14

Callableinterfejs deklaruje call()metodę i należy podać ogólne, ponieważ typ wywołania obiektu () powinien zwrócić -

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

Runnablez drugiej strony jest interfejs, który deklaruje run()metodę, która jest wywoływana podczas tworzenia wątku z runnable i wywołuje na nim start (). Możesz także bezpośrednio wywołać run (), ale to po prostu uruchamia metodę run () w tym samym wątku.

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

Podsumowując kilka znaczących różnic

  1. RunnableObiekt nie zwraca wynik podczas gdy Callableobiekt zwraca wynik.
  2. RunnableObiekt nie może rzucać sprawdzonej wyjątek ubezpieczeniowy Callableobiekt może rzucić wyjątek.
  3. RunnableInterfejs istnieje od Java 1.0 natomiast Callablezostał wprowadzony dopiero w Javie 1.5.

Kilka podobieństw obejmuje

  1. Wystąpienia klas implementujących interfejsy Runnable lub Callable są potencjalnie wykonywane przez inny wątek.
  2. Wystąpienie interfejsów wywoływalnych i uruchamialnych może być wykonane przez ExecutorService metodą subm ().
  3. Oba są interfejsami funkcjonalnymi i mogą być używane w wyrażeniach Lambda od Java8.

Metody w interfejsie ExecutorService są

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

13

Cel tych interfejsów z dokumentacji Oracle:

Runnable interfejs powinien być realizowany przez dowolną klasę, której instancje mają być realizowane przez Thread. Klasa musi zdefiniować metodę bez wywoływanych argumentów run.

Do wywołania : zadanie, które zwraca wynik i może zgłosić wyjątek. Implementatory definiują pojedynczą metodę bez argumentów nazywaną call. CallableInterfejs jest podobny do Runnable, ponieważ oba są przeznaczone dla klas, których przykłady są potencjalnie wykonywane przez inny gwint. A Runnablenie zwraca jednak wyniku i nie może zgłosić sprawdzonego wyjątku.

Inne różnice:

  1. Możesz przejść, Runnableaby utworzyć wątek . Ale nie można utworzyć nowego wątku, przekazując go Callablejako parametr. Można przekazać funkcję wywoływania tylko do ExecutorServiceinstancji.

    Przykład:

    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. Użyj Runnabledo ognia i zapominaj połączeń. Służy Callabledo weryfikacji wyniku.

  3. Callablemożna przekazać do metody invokeAll w przeciwieństwie do metody Runnable. Metody invokeAnyi invokeAllwykonywanie najbardziej przydatnych form wykonywania zbiorczego, wykonywanie zbioru zadań, a następnie oczekiwanie na ukończenie co najmniej jednego lub wszystkich

  4. Trywialna różnica: nazwa metody do zaimplementowania => run()for Runnablei call()for Callable.


11

Jak już wspomniano tutaj, Callable jest stosunkowo nowym interfejsem i został wprowadzony jako część pakietu współbieżności. Z programami wykonawczymi można używać zarówno wywoływalnych, jak i uruchamialnych. Wątek klasy (który implementuje sam Runnable) obsługuje tylko Runnable.

Nadal możesz używać Runnable z executorami. Zaletą Callable jest to, że możesz wysłać go do executora i natychmiast odzyskać przyszły wynik, który zostanie zaktualizowany po zakończeniu wykonywania. To samo można zaimplementować w Runnable, ale w takim przypadku musisz samodzielnie zarządzać wynikami. Na przykład możesz utworzyć kolejkę wyników, w której będą przechowywane wszystkie wyniki. Inny wątek może poczekać w tej kolejce i poradzić sobie z nadchodzącymi wynikami.


Zastanawiam się, jaki jest przykład wyjątku wyrzucającego wątek w Javie? czy główny wątek będzie w stanie wychwycić ten wyjątek? Jeśli nie, nie użyłbym Callable. Alex, masz na to jakiś wgląd? dzięki!
tryliony

1
Kod działający w niestandardowym wątku, jak każdy inny kod może zgłaszać wyjątek. Aby złapać go w innym wątku, musisz wykonać pewne wysiłki, używając niestandardowego mechanizmu powiadomień (np. FutureOpartego na słuchaczach) lub używając lub dodając hak, który przechwytuje wszystkie niewykrywane wyjątki: docs.oracle.com/javase/6/docs/api/ java / lang /…
AlexR

Świetna informacja! Dzięki, Alex! :)
tryliony

1
Poparłem tę odpowiedź, ponieważ zapewnia ona (poprawnie, jeśli jest pobierana według wartości nominalnej), należy użyć modelu puli wątków z obiektami, które można wywołać. Pozornie niefortunną rzeczą w tym jest to, że nie można rozszerzyć, Threadaby w znaczący sposób korzystać z Callableinterfejsu, tak aby można było dostosować pojedynczy wątek do robienia rzeczy na żądanie i innych rzeczy, których deweloper może chcieć. Jeśli ktoś, kto czyta ten komentarz, uważa, że ​​się mylę, chciałbym wiedzieć lepiej ...

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

Projektanci Java odczuli potrzebę rozszerzenia możliwości Runnableinterfejsu, ale nie chcieli wpływać na wykorzystanie Runnableinterfejsu i prawdopodobnie dlatego zdecydowali się na osobny interfejs o nazwie CallableJava 1.5, niż zmieniający już istniejący Runnableinterfejs, który jest częścią Java od Java 1.0. źródło


7

Różnice między wywoływalnymi i uruchamialnymi są następujące:

  1. Callable jest wprowadzony w JDK 5.0, ale Runnable jest wprowadzony w JDK 1.0
  2. Callable ma metodę call (), ale Runnable ma metodę run ().
  3. Callable ma metodę call, która zwraca wartość, ale Runnable ma metodę run, która nie zwraca żadnej wartości.
  4. Metoda wywołania może zgłosić sprawdzony wyjątek, ale metoda uruchomienia nie może zgłosić sprawdzonego wyjątku.
  5. Do wywołania kolejki zadań należy użyć metody subm (), ale można użyć metody execute (), aby umieścić w kolejce zadań.

Ważne jest, aby podkreślić, że zaznaczono wyjątek , a nie wyjątek RuntimeException
BertKing

5

Obie funkcje, które można wywoływać i wywoływać, są do siebie podobne i mogą być używane w implementacji wątku. W przypadku implementacji Runnable musisz zaimplementować metodę run () , ale w przypadku wywołania trzeba zaimplementować metodę call () , obie metody działają w podobny sposób, ale metoda call () ma większą elastyczność. Istnieją między nimi pewne różnice.

Różnica między Runnable i wymagalne jak below--

1) run () metoda przypisanymi zwrotu pustych , środków, jeśli chcesz coś powrotną wątek, który można wykorzystać dodatkowo wtedy masz żadnego wyboru z Runnable run () metody. Istnieje rozwiązanie „Callable”. Jeśli chcesz zwrócić dowolną rzecz w postaci obiektu , powinieneś użyć Callable zamiast Runnable . Interfejs na żądanie ma metodę „call ()”, która zwraca Object .

Podpis metody - Runnable->

public void run(){}

Do wywołania->

public Object call(){}

2) W przypadku Runnable run () metoda sprawdzona wyjątek jeśli powstaje wtedy trzeba trzeba obchodzić się z bloku try catch , ale w przypadku płatnych na żądanie call () metodę, która może rzucić wyjątek sprawdzenia jak poniżej

 public Object call() throws Exception {}

3) Runnable pochodzi ze starszej wersji java 1.0 , ale można ją wywołać w wersji Java 1.5 ze środowiskiem Executer .

Jeśli znasz Executorów , powinieneś użyć Callable zamiast Runnable .

Nadzieję, że rozumiesz.


2

Runnable (vs) Callable zaczyna działać, gdy używamy frameworka Executer.

ExecutorService to podinterface Executor, który akceptuje zadania Runnable i Callable.

Wcześniej wiele wątków można osiągnąć za pomocą interfejsu od wersji 1.0 , ale tutaj problemem jest to, że po wykonaniu zadania wątku nie jesteśmy w stanie zebrać informacji o wątkach. W celu gromadzenia danych możemy użyć pól statycznych.Runnable

Przykład Oddzielne wątki, aby zebrać dane każdego ucznia.

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

Aby rozwiązać ten problem, wprowadzili od wersji 1.5, która zwraca wynik i może zgłosić wyjątek.Callable<V>

  • Pojedyncza metoda abstrakcyjna : Zarówno interfejs wywoływalny, jak i uruchamialny mają jedną metodę abstrakcyjną, co oznacza, że ​​można jej używać w wyrażeniach lambda w Javie 8.

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

Istnieje kilka różnych sposobów delegowania zadań do wykonania w ExecutorService .

  • execute(Runnable task):void tworzy nowy wątek, ale nie blokuje głównego wątku lub wątku wywołującego, ponieważ ta metoda zwraca wartość void.
  • submit(Callable<?>):Future<?>, submit(Runnable):Future<?>tworzy nowy wątek i blokuje główny wątek, gdy używasz future.get () .

Przykład użycia interfejsów Runnable, Callable with 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();
    }
}

0

Jest to rodzaj konwencji nazewnictwa interfejsu, która pasuje do programowania funkcjonalnego

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

...
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.