Implémentation d’un runnable vs extension d’un thread

Implémentation d'un runnable ou extension d'un thread

1. introduction

«Dois-je implémenter unRunnable ou étendre la classeThread»? est une question assez commune.

Dans cet article, nous verrons quelle approche a le plus de sens dans la pratique et pourquoi.

2. Utilisation deThread

Définissons d'abord une classeSimpleThread qui étendThread:

public class SimpleThread extends Thread {

    private String message;

    // standard logger, constructor

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

Voyons également comment nous pouvons exécuter un thread de ce type:

@Test
public void givenAThread_whenRunIt_thenResult()
  throws Exception {

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

Nous pouvons également utiliser unExecutorService pour exécuter le thread:

@Test
public void givenAThread_whenSubmitToES_thenResult()
  throws Exception {

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

Cela représente beaucoup de code pour exécuter une seule opération de journal dans un thread séparé.

Notez également queSimpleThread cannot extend any other class, car Java ne prend pas en charge l'héritage multiple.

3. Implémentation d'unRunnable

Maintenant, créons une tâche simple qui implémente l'interfacejava.lang.Runnable:

class SimpleRunnable implements Runnable {

    private String message;

    // standard logger, constructor

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

LeSimpleRunnable ci-dessus est juste une tâche que nous voulons exécuter dans un thread séparé.

Il existe différentes approches que nous pouvons utiliser pour l'exécuter; l'un d'eux consiste à utiliser la classeThread:

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

On peut même utiliser unExecutorService:

@Test
public void givenARunnable_whenSubmitToES_thenResult()
 throws Exception {

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

Nous pouvons en savoir plus surExecutorService danshere.

Étant donné que nous implémentons actuellement une interface, nous sommes libres d'étendre une autre classe de base si nécessaire.

À partir de Java 8, toute interface exposant une seule méthode abstraite est traitée comme une interface fonctionnelle, ce qui en fait une cible d'expression lambda valide.

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?

En termes simples, nous encourageons généralement l'utilisation deRunnable surThread:

  • Lors de l'extension de la classeThread, nous ne remplaçons aucune de ses méthodes. Au lieu de cela, nous remplaçons la méthode deRunnable (whichThread implémente). Ceci est une violation flagrante du principe IS-AThread

  • Créer une implémentation deRunnable et la passer à la classeThread utilise la composition et non l'héritage - ce qui est plus flexible

  • Après avoir étendu la classeThread, nous ne pouvons étendre aucune autre classe

  • À partir de Java 8,Runnables peut être représenté sous forme d'expressions lambda

5. Conclusion

Dans ce rapide tutoriel, nous avons vu comment l'implémentation deRunnable est généralement une meilleure approche que l'extension de la classeThread.

Le code de ce message peut être trouvéover on GitHub.