Вопрос-ответ

"implements Runnable" vs "extends Thread" in Java

"реализует Runnable" против "extends Thread" в Java

За то время, что я провел с потоками в Java, я нашел эти два способа написания потоков:

С реализациями Runnable:

public class MyRunnable implements Runnable {
public void run() {
//Code
}
}
//Started with a "new Thread(new MyRunnable()).start()" call

Или с помощью extends Thread:

public class MyThread extends Thread {
public MyThread() {
super("MyThread");
}
public void run() {
//Code
}
}
//Started with a "new MyThread().start()" call

Есть ли какая-либо существенная разница в этих двух блоках кода?

Переведено автоматически
Ответ 1

Да: implements Runnable - предпочтительный способ сделать это, IMO. На самом деле вы не специализируете поведение потока. Вы просто предоставляете ему что-то для запуска. Это означает, что композиция является философски более "чистым" способом.

С практической точки зрения, это означает, что вы можете реализовать Runnable и расширять из другого класса, а также ... и вы также можете реализовать Runnable с помощью лямбда-выражения начиная с Java 8.

Ответ 2

tl; dr: реализация Runnable лучше. Однако важно предостережение.

В общем, я бы рекомендовал использовать что-то вроде Runnable, а не Thread потому что это позволяет вам поддерживать вашу работу только в слабой связи с вашим выбором параллелизма. Например, если вы используете Runnable и позже решите, что для этого на самом деле не требуется собственный Thread, вы можете просто вызвать ThreadA.run() .

Предостережение: здесь я настоятельно не рекомендую использовать необработанные потоки. Я предпочитаю использовать Callables и FutureTasks (Из javadoc: "Асинхронное вычисление с возможностью отмены"). Интеграция тайм-аутов, надлежащее аннулирование и объединение потоков с современной поддержкой параллелизма - все это для меня гораздо полезнее, чем кучи необработанных потоков.

Продолжение: Существует FutureTask конструктор, который позволяет вам использовать Runnables (если это то, что вам наиболее удобно) и при этом получать преимущества современных инструментов параллелизма. Процитирую javadoc:

Если вам не нужен конкретный результат, рассмотрите возможность использования конструкций вида:

Future<?> f = new FutureTask<Object>(runnable, null)

Итак, если мы заменим их runnable на ваши threadA, мы получим следующее:

new FutureTask<Object>(threadA, null)

Другой вариант, который позволяет вам оставаться ближе к Runnables, - это ThreadPoolExecutor. Вы можете использовать метод execute для передачи Runnable для выполнения "данной задачи когда-нибудь в будущем".

Если вы хотите попробовать использовать пул потоков, приведенный выше фрагмент кода будет выглядеть примерно следующим образом (используя Executors.newCachedThreadPool() заводской метод):

ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());
Ответ 3

Мораль истории:

Наследуется только в том случае, если вы хотите переопределить какое-либо поведение.

Или, скорее, это следует читать как:

Меньше наследуется, больше интерфейса.

Ответ 4

Если вы хотите реализовать или расширить любой другой класс, то Runnable интерфейс наиболее предпочтителен, в противном случае, если вы не хотите расширять или реализовывать какой-либо другой класс, то Thread предпочтительнее class .

Наиболее распространенным отличием является

введите описание изображения здесь

Когда вы extends Thread создаете класс, после этого вы не можете расширять любой другой класс, который вам требуется. (Как вы знаете, Java не позволяет наследовать более одного класса).

Когда вы implements Runnable, вы можете сэкономить место для своего класса, чтобы расширить любой другой класс в будущем или сейчас.


  • Java не поддерживает множественное наследование, что означает, что вы можете расширить только один класс в Java, поэтому, как только вы расширили класс Thread, вы потеряли свой шанс и не можете расширять или наследовать другой класс в Java.


  • В объектно-ориентированном программировании расширение класса обычно означает добавление новой функциональности и изменение или улучшение поведения. Если мы не вносим никаких изменений в поток, тогда вместо этого используем Runnable interface.


  • Интерфейс с возможностью выполнения представляет задачу, которая может выполняться либо обычным потоком, либо исполнителями, либо любыми другими средствами. таким образом, логическое разделение задачи как выполняемой, а не потока является хорошим дизайнерским решением.


  • Разделение задачи как доступной для выполнения означает, что мы можем повторно использовать задачу, а также можем выполнять ее другими способами. поскольку вы не можете перезапустить поток после его завершения. снова Runnable против Thread для задачи, Runnable выигрывает.


  • Java designer распознает это, и именно поэтому исполнители принимают Runnable как задачу, и у них есть рабочий поток, который выполняет эти задачи.


  • Наследование всех потоковых методов является дополнительной нагрузкой только для представления задачи, которая может быть легко выполнена с помощью Runnable.


Любезно предоставлено javarevisited.blogspot.com

Это были некоторые из заметных различий между Thread и Runnable в Java. Если вы знаете какие-либо другие различия между Thread и Runnable, пожалуйста, поделитесь ими в комментариях. Я лично использую Runnable over Thread для этого сценария и рекомендую использовать Runnable или вызываемый интерфейс в зависимости от ваших требований.

Однако существенная разница заключается в следующем.

Когда вы extends Thread создаете класс, каждый ваш поток создает уникальный объект и связывается с ним. Когда вы implements Runnable, он предоставляет один и тот же объект нескольким потокам.

java multithreading