"implements Runnable" против "extends Thread" в Java: что выбрать?
Я столкнулся с вопросом о том, как в Java реализовать потоки, и нашел два способа их создания:
- С использованием интерфейса
Runnable
:
public class MyRunnable implements Runnable {
public void run() {
// Код потокa
}
}
// Запускается с помощью вызова "new Thread(new MyRunnable()).start()"
- С использованием класса
Thread
:
public class MyThread extends Thread {
public MyThread() {
super("MyThread");
}
public void run() {
// Код потокa
}
}
// Запускается с помощью вызова "new MyThread().start()"
Возникает вопрос: есть ли какие-либо значительные различия между этими двумя подходами в использовании потоков?
3 ответ(ов)
Мораль истории:
Наследуйте только в том случае, если хотите переопределить какое-либо поведение.
Или, точнее, это можно понимать так:
Наследуйте реже, используйте интерфейсы чаще.
На самом деле, нерационально сравнивать Runnable
и Thread
друг с другом.
Эти два элемента имеют зависимость и взаимосвязь в многопоточности, подобно тому, как связаны колесо и двигатель в автомобиле.
Скажу так: для реализации многопоточности существует только один путь, состоящий из двух шагов. Позвольте объяснить свою точку зрения.
Runnable:
Когда вы реализуете интерфейс Runnable
, это означает, что вы создаете что-то, что может выполняться в другом потоке. Однако создание чего-то, что может работать внутри потока (выполняемого в потоке), не означает создание самого потока.
Таким образом, класс MyRunnable
является просто обычным классом с методом void run
. Его объекты будут обычными объектами с единственным методом run
, который будет выполняться нормально при вызове (если мы не передаем объект в поток).
Thread:
Класс Thread
— это, можно сказать, очень специализированный класс, обладающий возможностью запуска нового потока, который фактически обеспечивает многопоточность через свой метод start()
.
Почему нерационально сравнивать?
Потому что для многопоточности нам нужны оба этих компонента.
Для многопоточности нам нужно два вещи:
- Что-то, что может выполняться внутри потока (Runnable).
- Что-то, что может запустить новый поток (Thread).
Таким образом, технически и теоретически оба этих компонента необходимы для запуска потока: один будет выполнять, а другой — запускать (как колесо и двигатель в автомобиле).
Вот почему вы не можете запустить поток с помощью MyRunnable
; нужно передать его экземпляру Thread
.
Но вполне возможно создать и запустить поток, используя только класс Thread
, поскольку класс Thread
реализует Runnable
, так что нам известно, что Thread
также является Runnable
.
В итоге, Thread
и Runnable
дополняют друг друга в контексте многопоточности, а не конкурируют или заменяют друг друга.
Не являюсь экспертом, но могу предложить одну причину, почему стоит реализовать интерфейс Runnable вместо того, чтобы наследовать класс Thread: в Java поддерживается только однонаследование, так что вы можете наследовать только один класс.
Редактирование: Изначально здесь также упоминалось, что "реализация интерфейса требует меньше ресурсов", но вы всё равно должны создать новый экземпляр класса Thread, так что это было неверно.
Разница между "wait()" и "sleep()" в Java
Как работают сервлеты? Инстанцирование, сессии, общие переменные и многопоточность
Что значит 'synchronized'?
Как установить Java 8 на Mac
Почему в RecyclerView отсутствует onItemClickListener()?