Implementieren eines Runs gegen das Erweitern eines Threads

Implementieren eines Runnable im Vergleich zum Erweitern eines Threads

1. Einführung

„Soll ich einRunnable implementieren oder dieThread-Klasse erweitern?“ ist eine häufige Frage.

In diesem Artikel werden wir sehen, welcher Ansatz in der Praxis sinnvoller ist und warum.

2. Verwenden vonThread

Definieren wir zunächst eineSimpleThread-Klasse, dieThread erweitert:

public class SimpleThread extends Thread {

    private String message;

    // standard logger, constructor

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

Mal sehen, wie wir einen Thread dieses Typs ausführen können:

@Test
public void givenAThread_whenRunIt_thenResult()
  throws Exception {

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

Wir können auch einExecutorService verwenden, um den Thread auszuführen:

@Test
public void givenAThread_whenSubmitToES_thenResult()
  throws Exception {

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

Das ist ziemlich viel Code zum Ausführen eines einzelnen Protokollvorgangs in einem separaten Thread.

Beachten Sie außerdem, dassSimpleThread cannot extend any other class, da Java keine Mehrfachvererbung unterstützt.

3. Implementierung einesRunnable

Erstellen wir nun eine einfache Aufgabe, die diejava.lang.Runnable-Schnittstelle implementiert:

class SimpleRunnable implements Runnable {

    private String message;

    // standard logger, constructor

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

Das obigeSimpleRunnable ist nur eine Aufgabe, die wir in einem separaten Thread ausführen möchten.

Es gibt verschiedene Ansätze, mit denen wir es ausführen können. Eine davon ist die Verwendung der KlasseThread:

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

Wir können sogar einExecutorService verwenden:

@Test
public void givenARunnable_whenSubmitToES_thenResult()
 throws Exception {

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

Wir können mehr überExecutorService inhere lesen.

Da wir jetzt eine Schnittstelle implementieren, können wir bei Bedarf eine weitere Basisklasse erweitern.

Ab Java 8 wird jede Schnittstelle, die eine einzelne abstrakte Methode verfügbar macht, als funktionale Schnittstelle behandelt, wodurch sie zu einem gültigen Lambda-Ausdrucksziel wird.

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 oderThread?

Einfach ausgedrückt, empfehlen wir im Allgemeinen die Verwendung vonRunnable überThread:

  • Wenn Sie die KlasseThreaderweitern, überschreiben wir keine ihrer Methoden. Stattdessen überschreiben wir die Methode vonRunnable (, mit derThread) implementiert. Dies ist ein klarer Verstoß gegen das IS-AThread-Prinzip

  • Das Erstellen einer Implementierung vonRunnable und das Übergeben an die KlasseThread verwendet Komposition und nicht Vererbung - was flexibler ist

  • Nach dem Erweitern der KlasseThreadkönnen wir keine andere Klasse erweitern

  • Ab Java 8 könnenRunnables als Lambda-Ausdrücke dargestellt werden

5. Fazit

In diesem kurzen Tutorial haben wir gesehen, wie die Implementierung vonRunnable normalerweise besser ist als die Erweiterung derThread-Klasse.

Der Code für diesen Beitrag kannover on GitHub gefunden werden.