A diferença entre as interfaces Runnable e Callable em Java


Respostas:


444

Veja a explicação aqui .

A interface Callable é semelhante ao Runnable, pois ambas são projetadas para classes cujas instâncias são potencialmente executadas por outro thread. Um Runnable, no entanto, não retorna um resultado e não pode gerar uma exceção verificada.


270

Quais são as diferenças nas aplicações de Runnablee Callable. A diferença é apenas com o parâmetro de retorno presente em Callable?

Basicamente sim. Veja as respostas para esta pergunta . E o javadoc paraCallable .

Qual é a necessidade de ter os dois se Callablepode fazer tudo o que Runnablefaz?

Porque a Runnableinterface não pode fazer tudo o que Callablefaz!

Runnableexiste desde o Java 1.0, mas Callablefoi introduzido apenas no Java 1.5 ... para lidar com casos de uso que Runnablenão suportam. Em teoria, a equipe Java poderia ter alterado a assinatura do Runnable.run()método, mas isso quebraria a compatibilidade binária com o código anterior à 1.5, exigindo recodificação ao migrar o código Java antigo para as JVMs mais recentes. Isso é um GRANDE NÃO-NÃO. O Java se esforça para ser compatível com versões anteriores ... e esse tem sido um dos maiores pontos de venda do Java para computação comercial.

E, obviamente, existem casos de uso em que uma tarefa não precisa retornar um resultado ou lançar uma exceção verificada. Para esses casos de uso, usar Runnableé mais conciso do que usar Callable<Void>e retornar um nullvalor dummy ( ) do call()método


9
Eu me pergunto de onde você tirou essa história. Isso é muito útil.
spiderman

4
@prash - os fatos básicos podem ser encontrados em livros antigos. Como a primeira edição do Java em poucas palavras.
Stephen C

4
(@prash -. Também ... começando a usar Java na era Java 1.1)
Stephen C

1
@StephenC Se eu li sua resposta corretamente, você está sugerindo que ela Runnableexiste (em grande parte) por motivos de compatibilidade com versões anteriores. Mas não há situações em que é desnecessário ou muito caro implementar (ou exigir) Callableinterface (por exemplo, in ScheduledFuture<?> ScheduledExecutorService.schedule(Runnable command, long delay, TimeUnit unit))? Portanto, não há um benefício em manter as duas interfaces no idioma, mesmo que a história não force o resultado atual?
max

1
@max - Bem, eu disse isso, e ainda concordo com isso. No entanto, essa é uma razão secundária. Mas, mesmo assim, suspeito que Runnable teria sido modificado se não houvesse um imperativo para manter a compatibilidade. O "clichê" de return null;é um argumento fraco. (Pelo menos, essa teria sido a minha decisão ... no contexto hipotético onde você poderia ignorar compatibilidade com versões anteriores.)
Stephen C

82
  • Um Callableprecisa implementar o call()método enquanto outro Runnableprecisa implementar o run()método.
  • A Callablepode retornar um valor, mas Runnablenão pode.
  • Um Callablepode lançar exceção marcada, mas um Runnablenão pode.
  • A Callablepode ser usado com ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)métodos, mas a Runnablenão pode ser.

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

17
ExecutorService.submit (tarefa executável) também existe e é muito útil
Yair Kukielka 29/05

Executável pode também ser usado com ExecutorService seguindo maneiras-1) ExecutorService.execute (executável) 2) ExecutorService.submit (executável)
Azam Khan

2
Também existe Executor.submit (tarefa Callable <T>), mas você não pode invocarAll ou invokeAny com a coleção de tarefas Runnable Collection <? estende as tarefas Callable <T>>
nikli

36

Encontrei isso em outro blog que pode explicar um pouco mais essas diferenças :

Embora ambas as interfaces sejam implementadas pelas classes que desejam executar em um encadeamento de execução diferente, existem poucas diferenças entre as duas interfaces:

  • Uma Callable<V>instância retorna um resultado do tipo V, enquanto uma Runnableinstância não.
  • Uma Callable<V>instância pode gerar exceções verificadas, enquanto uma Runnableinstância não pode

Os designers de Java sentiram a necessidade de estender os recursos da Runnableinterface, mas não queriam afetar os usos da Runnableinterface e provavelmente essa foi a razão pela qual eles optaram por ter uma interface separada nomeada Callableno Java 1.5 do que alterar o já existente Runnable.


27

Vejamos onde alguém usaria Runnable e Callable.

Runnable e Callable são executados em um thread diferente do thread de chamada. Mas Callable pode retornar um valor e Runnable não. Então, onde isso realmente se aplica.

Runnable : Se você tiver uma tarefa de ignorar e esquecer, use Runnable. Coloque seu código dentro de um Runnable e, quando o método run () for chamado, você poderá executar sua tarefa. O segmento de chamada realmente não se importa quando você executa sua tarefa.

Chamada : Se você estiver tentando recuperar um valor de uma tarefa, use Chamada. Agora que pode ser chamado por si só não fará o trabalho. Você precisará de um futuro que envolva seu Callable e obtenha seus valores em future.get (). Aqui, o encadeamento de chamada será bloqueado até que o futuro volte com resultados que, por sua vez, aguardam a execução do método call () de Callable.

Portanto, pense em uma interface para uma classe de destino em que você tenha os métodos agrupados Runnable e Callable definidos. A classe de chamada aleatoriamente chamará os métodos da interface sem saber qual é Runnable e qual é Callable. Os métodos Runnable serão executados de forma assíncrona, até que um método Callable seja chamado. Aqui, o encadeamento da classe de chamada será bloqueado, pois você está recuperando valores da sua classe de destino.

NOTA: Dentro da classe de destino, você pode fazer chamadas para Callable e Runnable em um único executor de encadeamento, tornando esse mecanismo semelhante a uma fila de despacho serial. Desde que o chamador chame seus métodos agrupados Runnable, o thread de chamada será executado muito rápido, sem bloquear. Assim que chamar um método Callable envolvido no futuro, ele deverá ser bloqueado até que todos os outros itens na fila sejam executados. Somente então o método retornará com valores. Este é um mecanismo de sincronização.


14

Callableinterface declara call()método e você precisa fornecer genéricos, pois o tipo de chamada de objeto () deve retornar -

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

Runnablepor outro lado, é a interface que declara o run()método chamado quando você cria um Thread com o executável e chama start (). Você também pode chamar diretamente run (), mas que apenas executa o método run () é o mesmo encadeamento.

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

Para resumir algumas diferenças notáveis ​​são

  1. Um Runnableobjeto não retorna um resultado, enquanto um Callableobjeto retorna um resultado.
  2. Um Runnableobjeto não pode lançar uma exceção marcada, enquanto um Callableobjeto pode lançar uma exceção.
  3. A Runnableinterface existe desde o Java 1.0, enquanto Callablefoi introduzida apenas no Java 1.5.

Poucas semelhanças incluem

  1. Instâncias das classes que implementam interfaces Runnable ou Callable são potencialmente executadas por outro thread.
  2. A instância das interfaces Callable e Runnable pode ser executada pelo ExecutorService via método submit ().
  3. Ambas são interfaces funcionais e podem ser usadas em expressões Lambda desde o Java8.

Os métodos na interface ExecutorService são

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

13

Objetivo dessas interfaces da documentação da Oracle:

A interface executável deve ser implementada por qualquer classe cujas instâncias sejam executadas por a Thread. A classe deve definir um método sem argumentos chamados run.

Callable : uma tarefa que retorna um resultado e pode gerar uma exceção. Os implementadores definem um único método sem argumentos chamados de chamada. A Callableinterface é semelhante Runnable, pois ambas são projetadas para classes cujas instâncias são potencialmente executadas por outro encadeamento. UMARunnable , no entanto, não retorna um resultado e não pode gerar uma exceção verificada.

Outras diferenças:

  1. Você pode passar Runnablepara criar um Thread . Mas você não pode criar um novo thread passando Callablecomo parâmetro. Você pode passar Callable apenas para ExecutorServiceinstâncias.

    Exemplo:

    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. Use Runnablepara fogo e esqueça as chamadas. Use Callablepara verificar o resultado.

  3. Callablepode ser passado para invokeAll método diferente Runnable. Métodos invokeAnye invokeAllexecute as formas mais úteis de execução em massa, executando uma coleção de tarefas e aguardando pelo menos uma, ou todas, para concluir

  4. Diferença trivial: nome do método a ser implementado => run()para Runnablee call()para Callable.


11

Como já foi mencionado aqui, Callable é uma interface relativamente nova e foi introduzida como parte do pacote de simultaneidade. Callable e Runnable podem ser usados ​​com executores. O thread de classe (que implementa o próprio Runnable) oferece suporte apenas ao Runnable.

Você ainda pode usar o Runnable com executores. A vantagem do Callable é que você pode enviá-lo ao executor e retornar imediatamente o resultado futuro que será atualizado quando a execução for concluída. O mesmo pode ser implementado com o Runnable, mas nesse caso você precisa gerenciar os resultados sozinho. Por exemplo, você pode criar uma fila de resultados que conterá todos os resultados. Outro encadeamento pode esperar nessa fila e lidar com os resultados que chegarem.


Gostaria de saber qual é o exemplo em um segmento lançando exceção em java? o thread principal será capaz de capturar essa exceção? Caso contrário, eu não usaria Callable. Alex, você tem alguma ideia disso? obrigado!
trilhões

1
Código em execução no encadeamento personalizado, pois qualquer outro código pode gerar exceção. Para capturá-lo em outro encadeamento, você deve executar alguns esforços usando o mecanismo de notificação personalizado (por exemplo, com base em ouvintes) ou usando Futureou adicionando gancho que captura todas as exceções não encontradas: docs.oracle.com/javase/6/docs/api/ java / lang /…
AlexR

Ótima informação! Obrigado Alex! :)
trilhões

1
Votei positivamente nesta resposta porque ela afirma (corretamente, se tirada pelo valor nominal) que você deve usar o modelo de pool de threads com objetos que podem ser chamados. A coisa aparentemente lamentável sobre isso é que não se pode estender o Threaduso significativo da Callableinterface para que um único encadeamento possa ser personalizado para fazer coisas que podem ser chamadas e outras que o desenvolvedor possa querer. Se qualquer um que lê este comentário pensa que eu estou errado, eu gostaria de conhecer melhor ...

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

Os designers de Java sentiram a necessidade de estender os recursos da Runnableinterface, mas não queriam afetar os usos da Runnableinterface e provavelmente essa foi a razão pela qual eles optaram por ter uma interface separada nomeada Callableno Java 1.5 do que alterar o já Runnableinterface existente que faz parte do Java desde o Java 1.0. fonte


7

As diferenças entre Callable e Runnable são as seguintes:

  1. Callable é introduzido no JDK 5.0, mas Runnable é introduzido no JDK 1.0
  2. Callable possui o método call (), mas Runnable possui o método run ().
  3. Callable possui um método de chamada que retorna valor, mas Runnable possui um método de execução que não retorna nenhum valor.
  4. O método call pode gerar exceção verificada, mas o método run não pode gerar exceção verificada.
  5. O método callable submit () usa para colocar na fila de tarefas, mas o Runnable usa o método execute () para colocar na fila de tarefas.

É importante enfatizar que a exceção marcada , não a RuntimeException
BertKing,

5

Callable e Runnable são semelhantes entre si e podem ser usados ​​na implementação de threads. No caso de implementar o Runnable, você deve implementar o método run () , mas, no caso de callable, deve implementar o método call () , ambos os métodos funcionam de maneira semelhante, mas callable () método tem mais flexibilidade. Existem algumas diferenças entre eles.

Diferença entre Runnable e Callable como abaixo--

1) O método run () de runnable retorna nulo , significa que se você deseja que seu thread retorne algo que possa ser usado mais, então você não tem escolha com o método runnable run () . Existe uma solução 'Callable' . Se você quiser retornar qualquer coisa na forma de objeto , use Callable em vez de Runnable . Interface callable tem o método 'call ()' que retorna Object .

Assinatura do método - Executável->

public void run(){}

Chamadas->

public Object call(){}

2) No caso do método runnable run () , se surgir alguma exceção verificada, você deve precisar lidar com o bloco try catch , mas, no caso do método Callable call () , é possível lançar a exceção verificada conforme abaixo

 public Object call() throws Exception {}

3) Runnable vem da versão legada do java 1.0 , mas callable veio na versão Java 1.5 com o Executer estrutura .

Se você estiver familiarizado com Executores , use Callable em vez de Runnable .

Espero que você entenda.


2

Executável (vs) Callable entra em quando estamos usando a estrutura do Executer.

ExecutorService é uma subinterface de Executor, que aceita tarefas Runnable e Callable.

O Multi-Threading anterior pode ser alcançado usando a Interface Desde 1.0 , mas aqui o problema ocorre após a conclusão da tarefa de encadeamento, não podemos coletar as informações dos Encadeamentos. Para coletar os dados, podemos usar campos estáticos.Runnable

Exemplo Segmentos separados para coletar os dados de cada aluno.

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

Para resolver esse problema, eles introduziram o From 1.5, que retorna um resultado e pode gerar uma exceção.Callable<V>

  • Método abstrato único : As interfaces Callable e Runnable têm um único método abstrato, o que significa que podem ser usadas em expressões lambda no java 8.

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

Existem algumas maneiras diferentes de delegar tarefas para execução em um ExecutorService .

  • execute(Runnable task):void cria novo encadeamento, mas não bloqueia o encadeamento principal ou o chamador, pois esse método retorna nulo.
  • submit(Callable<?>):Future<?>, submit(Runnable):Future<?>cria novo encadeamento e bloqueia o encadeamento principal quando você estiver usando future.get () .

Exemplo de uso da interface Interfaces executável, configurável com estrutura de 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

É um tipo de convenção de nomenclatura de interface que combina com a programação funcional

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

...
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.