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 .