Рубрики
Без рубрики

Реализация запускаемого и расширение потока

Узнайте, почему реализация Runnable является лучшим подходом, чем расширение класса потоков.

Автор оригинала: baeldung.

1. введение

“Должен ли я реализовать Запускаемый или расширить Поток класс”? это довольно распространенный вопрос.

В этой статье мы увидим, какой подход имеет больше смысла на практике и почему.

2. Использование резьбы

Давайте сначала определим Простой поток класс, который расширяет Поток :

public class SimpleThread extends Thread {

    private String message;

    // standard logger, constructor

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

Давайте также посмотрим, как мы можем запустить поток такого типа:

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

Мы также можем использовать ExecutorService для выполнения потока:

@Test
public void givenAThread_whenSubmitToES_thenResult()
  throws Exception {
    
    executorService.submit(new SimpleThread(
      "SimpleThread executed using ExecutorService")).get();
}

Это довольно много кода для выполнения одной операции журнала в отдельном потоке.

Кроме того, обратите внимание , что Простой поток не может расширять какой-либо другой класс , так как Java не поддерживает множественное наследование.

3. Реализация управляемого

Теперь давайте создадим простую задачу, которая реализует java.lang.Запускаемый интерфейс:

class SimpleRunnable implements Runnable {
	
    private String message;
	
    // standard logger, constructor
    
    @Override
    public void run() {
        log.info(message);
    }
}

Вышеприведенный Simple Runnable – это просто задача, которую мы хотим запустить в отдельном потоке.

Существуют различные подходы, которые мы можем использовать для его запуска; один из них-использовать класс Thread :

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

Мы даже можем использовать ExecutorService :

@Test
public void givenARunnable_whenSubmitToES_thenResult()
 throws Exception {
    
    executorService.submit(new SimpleRunnable(
      "SimpleRunnable executed using ExecutorService")).get();
}

Мы можем прочитать больше о ExecutorService в здесь .

Поскольку мы сейчас реализуем интерфейс, мы можем расширить другой базовый класс, если нам это нужно.

Начиная с Java 8, любой интерфейс, предоставляющий один абстрактный метод, рассматривается как функциональный интерфейс, что делает его допустимым целевым объектом лямбда-выражения.

Мы можем переписать приведенный выше Выполняемый код, используя лямбда-выражение :

@Test
public void givenARunnableLambda_whenSubmitToES_thenResult() 
  throws Exception {
    
    executorService.submit(
      () -> log.info("Lambda runnable executed!"));
}

4. Запускаемый или потоковый?

Проще говоря, мы обычно поощряем использование Runnable over Thread :

  • При расширении класса Thread мы не переопределяем ни один из его методов. Вместо этого мы переопределяем метод Runnable ( который Thread реализует ) . Это явное нарушение IS-A Нить принцип
  • Создание реализации Runnable и передача ее в класс Thread использует композицию, а не наследование, что является более гибким
  • После расширения класса Thread мы не можем расширить какой – либо другой класс
  • Начиная с Java 8, Runnables могут быть представлены в виде лямбда-выражений

5. Заключение

В этом кратком руководстве мы увидели, как реализация Runnable обычно является лучшим подходом, чем расширение класса Thread .

Код для этого поста можно найти на GitHub .