Implementando um Runnable vs Estendendo um Thread

Implementando um Runnable vs Estendendo um Thread

1. Introdução

“Devo implementar umRunnable ou estender a classeThread”? é uma pergunta bastante comum.

Neste artigo, veremos qual abordagem faz mais sentido na prática e por quê.

2. UsandoThread

Vamos primeiro definir uma classeSimpleThread que estendeThread:

public class SimpleThread extends Thread {

    private String message;

    // standard logger, constructor

    @Override
    public void run() {
        log.info(message);
    }
}

Vejamos também como podemos executar um thread desse tipo:

@Test
public void givenAThread_whenRunIt_thenResult()
  throws Exception {

    Thread thread = new SimpleThread(
      "SimpleThread executed using Thread");
    thread.start();
    thread.join();
}

Também podemos usar umExecutorService para executar o thread:

@Test
public void givenAThread_whenSubmitToES_thenResult()
  throws Exception {

    executorService.submit(new SimpleThread(
      "SimpleThread executed using ExecutorService")).get();
}

Isso é uma grande quantidade de código para executar uma única operação de log em um thread separado.

Além disso, observe queSimpleThread cannot extend any other class, já que Java não oferece suporte a herança múltipla.

3. Implementando umRunnable

Agora, vamos criar uma tarefa simples que implementa a interfacejava.lang.Runnable:

class SimpleRunnable implements Runnable {

    private String message;

    // standard logger, constructor

    @Override
    public void run() {
        log.info(message);
    }
}

OSimpleRunnable acima é apenas uma tarefa que queremos executar em uma thread separada.

Existem várias abordagens que podemos usar para executá-lo; um deles é usar a classeThread:

@Test
public void givenRunnable_whenRunIt_thenResult()
 throws Exception {
    Thread thread = new Thread(new SimpleRunnable(
      "SimpleRunnable executed using Thread"));
    thread.start();
    thread.join();
}

Podemos até usar umExecutorService:

@Test
public void givenARunnable_whenSubmitToES_thenResult()
 throws Exception {

    executorService.submit(new SimpleRunnable(
      "SimpleRunnable executed using ExecutorService")).get();
}

Podemos ler mais sobreExecutorService emhere.

Já que agora estamos implementando uma interface, estamos livres para estender outra classe base se necessário.

A partir do Java 8, qualquer interface que expõe um único método abstrato é tratada como uma interface funcional, o que o torna um destino de expressão lambda válido.

We can rewrite the above Runnable code using a lambda expression:

@Test
public void givenARunnableLambda_whenSubmitToES_thenResult()
  throws Exception {

    executorService.submit(
      () -> log.info("Lambda runnable executed!"));
}

4. Runnable ouThread?

Simplificando, geralmente encorajamos o uso deRunnable em vez deThread:

  • Ao estender a classeThread, não substituímos nenhum de seus métodos. Em vez disso, substituímos o método deRunnable (whichThread implementa). Esta é uma violação clara do princípio IS-AThread

  • Criar uma implementação deRunnablee passá-la para a classeThread utiliza composição e não herança - que é mais flexível

  • Depois de estender a classeThread, não podemos estender nenhuma outra classe

  • Do Java 8 em diante,Runnables pode ser representado como expressões lambda

5. Conclusão

Neste tutorial rápido, vimos como implementarRunnable é normalmente uma abordagem melhor do que estender a classeThread.

O código desta postagem pode ser encontradoover on GitHub.