"реализует Runnable" против "расширяет поток" в Java
Из того, сколько времени я провел с потоками в Java, я нашел два способа написания потоков:
С implements 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
Есть ли существенная разница в этих двух блоках кода?
45 ответов
Да: реализует Runnable
это предпочтительный способ сделать это, ИМО. Вы на самом деле не специализируете поведение потока. Вы просто даете ему что-то, чтобы бежать. Это означает, что композиция - это философски более чистый путь.
На практике это означает, что вы можете реализовать Runnable
и простираться от другого класса также.
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());
Мораль истории:
Наследовать, только если вы хотите переопределить некоторое поведение.
Или, скорее, это следует читать как:
Унаследуй меньше, интерфейс больше.
Ну так много хороших ответов, я хочу добавить еще об этом. Это поможет понять Extending v/s Implementing Thread
,
Extends очень тесно связывает два файла классов и может привести к некоторым трудностям при работе с кодом.
Оба подхода выполняют одну и ту же работу, но есть некоторые различия.
Наиболее распространенная разница
- Когда вы расширяете класс Thread, после этого вы не можете расширять любой другой класс, который вам требуется. (Как вы знаете, Java не позволяет наследовать более одного класса).
- Когда вы реализуете Runnable, вы можете сэкономить место для вашего класса, чтобы расширить любой другой класс в будущем или сейчас.
Однако одно существенное различие между реализацией Runnable и расширением Thread заключается в том, что
by extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instance.
Следующий пример поможет вам понять более четко
//Implement Runnable Interface...
class ImplementsRunnable implements Runnable {
private int counter = 0;
public void run() {
counter++;
System.out.println("ImplementsRunnable : Counter : " + counter);
}
}
//Extend Thread class...
class ExtendsThread extends Thread {
private int counter = 0;
public void run() {
counter++;
System.out.println("ExtendsThread : Counter : " + counter);
}
}
//Use above classes here in main to understand the differences more clearly...
public class ThreadVsRunnable {
public static void main(String args[]) throws Exception {
// Multiple threads share the same object.
ImplementsRunnable rc = new ImplementsRunnable();
Thread t1 = new Thread(rc);
t1.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
Thread t2 = new Thread(rc);
t2.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
Thread t3 = new Thread(rc);
t3.start();
// Creating new instance for every thread access.
ExtendsThread tc1 = new ExtendsThread();
tc1.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
ExtendsThread tc2 = new ExtendsThread();
tc2.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
ExtendsThread tc3 = new ExtendsThread();
tc3.start();
}
}
Вывод вышеуказанной программы.
ImplementsRunnable : Counter : 1
ImplementsRunnable : Counter : 2
ImplementsRunnable : Counter : 3
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1
В подходе интерфейса Runnable создается только один экземпляр класса, и он используется разными потоками. Таким образом, значение счетчика увеличивается для каждого доступа к потоку.
Принимая во внимание, что подход класса Thread, вы должны создать отдельный экземпляр для каждого доступа потока. Следовательно, для каждого экземпляра класса выделяется разная память, и у каждого есть отдельный счетчик, значение остается тем же, что означает, что никакого приращения не произойдет, потому что ни одна из ссылок на объект не является одинаковой.
Когда использовать Runnable?
Используйте интерфейс Runnable, когда вы хотите получить доступ к тому же ресурсу из группы потоков. Избегайте использования класса Thread здесь, потому что создание нескольких объектов потребляет больше памяти, и это приводит к значительному снижению производительности.
Класс, который реализует Runnable, не является потоком, а просто классом. Чтобы Runnable стал потоком, вам нужно создать экземпляр потока и передать себя в качестве цели.
В большинстве случаев интерфейс Runnable следует использовать, если вы планируете только переопределить run()
метод и никаких других потоковых методов. Это важно, потому что классы не должны быть разделены на подклассы, если программист не намеревается изменить или улучшить фундаментальное поведение класса.
Когда необходимо расширить суперкласс, реализация интерфейса Runnable более подходит, чем использование класса Thread. Потому что мы можем расширить другой класс при реализации интерфейса Runnable для создания потока.
Я надеюсь, это поможет!
Если вы хотите реализовать или расширить любой другой класс, то Runnable
Интерфейс является наиболее предпочтительным, если вы не хотите, чтобы какой-либо другой класс расширял или реализовывал Thread
класс предпочтительнее
Наиболее распространенная разница
Когда ты extends Thread
класс, после этого вы не можете расширить любой другой класс, который вам требуется. (Как вы знаете, Java не позволяет наследовать более одного класса).
Когда ты implements Runnable
, вы можете сэкономить место для вашего класса, чтобы расширить любой другой класс в будущем или сейчас.
Java не поддерживает множественное наследование, что означает, что вы можете расширить только один класс в Java, поэтому после расширения класса Thread вы потеряете свой шанс и не сможете расширять или наследовать другой класс в Java.
В объектно-ориентированном программировании расширение класса обычно означает добавление новых функций, изменение или улучшение поведения. Если мы не вносим никаких изменений в Thread, используйте вместо этого интерфейс Runnable.
Runnable интерфейс представляет собой задачу, которая может быть выполнена либо простым потоком, либо исполнителями, либо любым другим способом. так что логическое разделение Task как Runnable чем Thread является хорошим дизайнерским решением.
Разделение задачи как Runnable означает, что мы можем повторно использовать задачу, а также можем выполнять ее различными способами. так как вы не можете перезапустить поток после его завершения. снова Runnable vs Thread для задачи, Runnable - победитель.
Java-дизайнер признает это, и поэтому исполнители принимают Runnable как Task, и у них есть рабочий поток, который выполняет эту задачу.
Наследование всех методов Thread - это дополнительные издержки только для представления Задачи, которые можно легко выполнить с помощью Runnable.
Предоставлено javarevisited.blogspot.com
Это были некоторые заметные различия между Thread и Runnable в Java, если вы знаете какие-либо другие различия между Thread и Runnable, чем делитесь ими через комментарии. Я лично использую Runnable поверх Thread для этого сценария и рекомендую использовать интерфейс Runnable или Callable в зависимости от ваших требований.
Однако существенная разница есть.
Когда ты extends Thread
класс, каждый ваш поток создает уникальный объект и ассоциируется с ним. Когда ты implements Runnable
, он разделяет один и тот же объект на несколько потоков.
Одна вещь, которую я удивлен, еще не упоминалась, это то, что реализация Runnable
делает ваш класс более гибким.
Если вы расширяете поток, то действие, которое вы делаете, всегда будет в потоке. Тем не менее, если вы реализуете Runnable
это не должно быть. Вы можете запустить его в потоке, или передать его какому-то сервису-исполнителю, или просто передать как задачу в рамках одного многопоточного приложения (возможно, для запуска в более позднее время, но в том же потоке). Варианты намного более открыты, если вы просто используете Runnable
чем если бы вы связали себя с Thread
,
На самом деле, не стоит сравнивать Runnable
а также Thread
друг с другом.
Эти два имеют зависимость и отношения в многопоточности так же, как Wheel and Engine
автомобильные отношения.
Я бы сказал, что есть только один способ многопоточности с двумя шагами. Позвольте мне высказать свое мнение.
Runnable:
При реализации interface Runnable
это означает, что вы создаете что-то, что run able
в другой теме. Теперь создание чего-либо, что может выполняться внутри потока (может выполняться внутри потока), не означает создание потока.
Итак, класс MyRunnable
не что иное, как обычный класс с void run
метод. И это объекты будут некоторые обычные объекты только с методом run
который будет нормально выполняться при вызове. (если мы не передадим объект в потоке).
Нить: class Thread
Я бы сказал, что это очень специальный класс с возможностью запуска нового потока, который фактически позволяет многопоточность через его start()
метод.
Почему не стоит сравнивать?
Потому что нам нужны они оба для многопоточности.
Для многопоточности нам нужны две вещи:
- Что-то, что может работать внутри потока (Runnable).
- То, что может начать новую тему (Thread).
Так что технически и теоретически оба они необходимы для запуска потока, один будет запущен, а другой заставит его работать (как Wheel and Engine
автомашины).
Вот почему вы не можете начать тему с MyRunnable
вам нужно передать его экземпляру Thread
,
Но создать и запустить поток можно только используя class Thread
потому что класс Thread
инвентарь Runnable
так что мы все знаем Thread
также является Runnable
внутри.
в заключение Thread
а также Runnable
дополняют друг друга для многопоточности не конкурента или замены.
Вы должны реализовать Runnable, но если вы работаете на Java 5 или выше, вы не должны запускать его с new Thread
но используйте вместо этого ExecutorService. Подробнее см.: Как реализовать простую многопоточность в Java.
Я не эксперт, но я могу придумать одну причину для реализации Runnable вместо расширения Thread: Java поддерживает только одиночное наследование, поэтому вы можете расширять только один класс.
Редактировать: изначально говорилось "Реализация интерфейса требует меньше ресурсов". также, но вам нужно создать новый экземпляр Thread в любом случае, так что это было неправильно.
Я бы сказал, что есть третий путь:
public class Something {
public void justAnotherMethod() { ... }
}
new Thread(new Runnable() {
public void run() {
instanceOfSomething.justAnotherMethod();
}
}).start();
Возможно, на это немного повлияло мое недавнее интенсивное использование Javascript и Actionscript 3, но в этом случае вашему классу не нужно реализовывать довольно расплывчатый интерфейс, такой как Runnable
,
С выпуском Java 8 появился третий вариант.
Runnable
является функциональным интерфейсом, что означает, что его экземпляры могут быть созданы с помощью лямбда-выражений или ссылок на методы.
Ваш пример можно заменить на:
new Thread(() -> { /* Code here */ }).start()
или если вы хотите использовать ExecutorService
и ссылка на метод:
executor.execute(runner::run)
Они не только намного короче ваших примеров, но и обладают многими преимуществами, изложенными в других ответах на использование. Runnable
над Thread
например, единоличная ответственность и использование композиции, потому что вы не специализируете поведение потока. Этот способ также позволяет избежать создания дополнительного класса, если все, что вам нужно, это Runnable
как вы делаете в своих примерах.
Создание интерфейса дает более четкое разделение между вашим кодом и реализацией потоков, поэтому в этом случае я бы предпочел реализовать Runnable.
Кажется, все здесь думают, что реализация Runnable - это путь, и я на самом деле не согласен с ними, но, на мой взгляд, есть также причина для расширения Thread, на самом деле вы как бы продемонстрировали это в своем коде.
Если вы реализуете Runnable, то класс, реализующий Runnable, не будет контролировать имя потока, это вызывающий код, который может установить имя потока, например, так:
new Thread(myRunnable,"WhateverNameiFeelLike");
но если вы расширяете Thread, тогда вы можете управлять этим внутри самого класса (как в вашем примере вы называете поток "ThreadB"). В этом случае вы:
А) может дать ему более полезное имя для целей отладки
B) принуждают использовать это имя для всех экземпляров этого класса (если вы не игнорируете тот факт, что он является потоком, и делаете вышеизложенное с ним, как будто это Runnable, но мы говорим о соглашении здесь в любом случае, поэтому можем игнорировать эту возможность я чувствую).
Вы можете даже, например, взять трассировку стека его создания и использовать его в качестве имени потока. Это может показаться странным, но в зависимости от структуры вашего кода это может быть очень полезно для целей отладки.
Это может показаться небольшим, но если у вас очень сложное приложение с большим количеством потоков и все внезапно "остановилось" (либо по причине тупика, либо, возможно, из-за ошибки в сетевом протоколе, который был бы меньше очевидно - или по другим бесконечным причинам) тогда получение дампа стека из Java, где все потоки называются "Thread-1", "Thread-2", "Thread-3", не всегда очень полезно (это зависит от того, как ваши потоки структурирован и можете ли вы с пользой определить, что есть что, просто по их трассировке стека - не всегда возможно, если вы используете группы из нескольких потоков, все выполняющие один и тот же код).
Сказав, что вы, конечно, также можете сделать вышеупомянутое в общем виде, создав расширение класса потока, которое устанавливает его имя в виде трассировки стека его вызова создания, а затем используйте это с вашими реализациями Runnable вместо стандартного класса потока Java. (см. ниже), но в дополнение к трассировке стека может быть более специфичная для контекста информация, которая будет полезна в имени потока для отладки (ссылка на одну из множества очередей или сокетов, которые он может обработать, например, в этом случае вы можете предпочесть расширить Thread специально для этого случая, чтобы вы могли заставить компилятор заставить вас (или других пользователей, использующих ваши библиотеки) передавать определенную информацию (например, нужную очередь / сокет) для использования в имени).
Вот пример общего потока с трассировкой вызывающего стека в качестве имени:
public class DebuggableThread extends Thread {
private static String getStackTrace(String name) {
Throwable t= new Throwable("DebuggableThread-"+name);
ByteArrayOutputStream os = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(os);
t.printStackTrace(ps);
return os.toString();
}
public DebuggableThread(String name) {
super(getStackTrace(name));
}
public static void main(String[] args) throws Exception {
System.out.println(new Thread());
System.out.println(new DebuggableThread("MainTest"));
}
}
и вот пример выходных данных, сравнивающих два имени:
Thread[Thread-1,5,main]
Thread[java.lang.Throwable: DebuggableThread-MainTest
at DebuggableThread.getStackTrace(DebuggableThread.java:6)
at DebuggableThread.<init>(DebuggableThread.java:14)
at DebuggableThread.main(DebuggableThread.java:19)
,5,main]
Runnable потому что:
- Предоставляет большую гибкость реализации Runnable для расширения другого класса
- Отделяет код от исполнения
- Позволяет вам запустить ваш runnable из пула потоков, потока событий или любым другим способом в будущем.
Даже если вам не нужно ничего из этого сейчас, вы можете в будущем. Поскольку переопределение Thread не дает никаких преимуществ, Runnable является лучшим решением.
Поскольку это очень популярная тема, и хорошие ответы распространяются повсеместно и рассматриваются очень подробно, я чувствовал, что было бы оправданным обобщать хорошие ответы от других в более сжатую форму, чтобы у новичков был простой обзор:
Вы обычно расширяете класс для добавления или изменения функциональности. Итак, если вы не хотите перезаписывать какое-либо поведение потока, используйте Runnable.
В то же время, если вам не нужно наследовать методы потоков, вы можете обойтись без этих накладных расходов, используя Runnable.
Одиночное наследование: если вы расширяете Thread, вы не можете расширяться из любого другого класса, поэтому, если это то, что вам нужно сделать, вы должны использовать Runnable.
Это хорошая идея, чтобы отделить доменную логику от технических средств, в этом смысле лучше иметь задачу Runnable, изолирующую вашу задачу от вашего бегуна.
Вы можете выполнить один и тот же объект Runnable несколько раз, однако объект Thread можно запустить только один раз. (Может быть, причина, почему Исполнители принимают Runnables, но не Threads.)
Если вы разрабатываете свою задачу как Runnable, у вас есть все возможности использовать ее сейчас и в будущем. Вы можете запустить его одновременно через Executors, а также через Thread. И вы все равно можете использовать / вызывать его не одновременно в том же потоке, как любой другой обычный тип / объект.
Это также облегчает разделение логики задач и аспектов параллелизма в ваших модульных тестах.
Если вас интересует этот вопрос, вас также может заинтересовать разница между Callable и Runnable.
- Java не поддерживает множественное наследование, что означает, что вы можете расширять только один класс в Java, поэтому, как только вы расширили
Thread
класс вы потеряли свой шанс и не можете расширять или наследовать другой класс в Java. - В объектно-ориентированном программировании расширение класса обычно означает добавление новых функций, изменение или улучшение поведения. Если мы не вносим никаких изменений в
Thread
чем использоватьRunnable
интерфейс вместо. Runnable
интерфейс представляет собойTask
который может быть выполнен любой простойThread
или жеExecutors
или любым другим способом. Столь логичное разделениеTask
какRunnable
чемThread
это хорошее дизайнерское решение.- Разделение задачи как
Runnable
означает, что мы можем повторно использовать задачу, а также можем выполнять ее различными способами. Так как вы не можете перезапуститьThread
как только это завершится, сноваRunnable
противThread
для задачи,Runnable
победитель - Java-дизайнер это понимает и поэтому
Executors
приниматьRunnable
какTask
и у них есть рабочий поток, который выполняет эту задачу. - Наследуя все
Thread
методы являются дополнительными накладными расходами только для представленияTask
что можно легко сделать сRunnable
,
Это обсуждается в руководстве Oracle " Определение и запуск потока":
Какой из этих идиом вы должны использовать? Первая идиома, в которой используется объект Runnable, является более общей, поскольку объект Runnable может создавать подкласс класса, отличного от Thread. Второй идиом проще использовать в простых приложениях, но он ограничен тем, что ваш класс задач должен быть потомком Thread. Этот урок посвящен первому подходу, который отделяет задачу Runnable от объекта Thread, который выполняет задачу. Этот подход не только более гибок, но и применим к высокоуровневым API-интерфейсам управления потоками, которые будут рассмотрены ниже.
Другими словами, реализация Runnable
будет работать в сценариях, где ваш класс расширяет класс, кроме Thread
, Java не поддерживает множественное наследование. Также, расширяя Thread
будет невозможно при использовании некоторых высокоуровневых API-интерфейсов управления потоками. Единственный сценарий, где расширение Thread
предпочтительно в небольшом приложении, которое не будет обновляться в будущем. Это почти всегда лучше реализовать Runnable
так как он становится более гибким по мере роста вашего проекта. Изменение дизайна не будет иметь большого влияния, так как вы можете реализовать много интерфейсов в Java, но расширить только один класс.
Самое простое объяснение было бы путем реализации Runnable
мы можем назначить один и тот же объект нескольким потокам и каждому Thread
разделяет те же состояния объекта и поведение.
Например, предположим, что есть два потока, thread1 помещает целое число в массив, а thread2 берет целые числа из массива, когда массив заполняется. Обратите внимание, что для работы thread2 необходимо знать состояние массива, заполнил ли thread1 или нет.
Внедрение Runnable
позволяет вам иметь эту гибкость, чтобы поделиться объектом, тогда как extends Thread
заставляет вас создавать новые объекты для каждого потока, поэтому любое обновление, выполняемое thread1, теряется в thread2.
Если я не ошибаюсь, это более или менее похоже на
В чем разница между интерфейсом и абстрактным классом?
расширяет, устанавливает отношение "Is A", а интерфейс предоставляет возможность "имеет".
Предпочитаю реализовать Runnable:
- Если вам не нужно расширять класс Thread и изменять реализацию по умолчанию Thread API
- Если вы выполняете огонь и забыли команду
- Если вы уже продлеваете другой класс
Предпочитаю "расширяет тему":
- Если вам нужно переопределить любой из этих методов Thread, как указано на странице документации Oracle
Как правило, вам не нужно переопределять поведение потока. Так что реализует Runnable предпочтительнее для большинства случаев.
С другой стороны, используя расширенный ExecutorService
или же ThreadPoolExecutorService
API обеспечивает большую гибкость и контроль.
Посмотрите на этот вопрос SE:
Можем ли мы еще раз посетить основную причину, по которой мы хотели, чтобы наш класс вел себя как Thread
? Нет никакой причины, мы просто хотели выполнить задачу, скорее всего, в асинхронном режиме, что точно означает, что выполнение задачи должно происходить из нашего основного потока и основного потока, если он заканчивается рано, может ждать или не ждать для разветвленного пути (задача).
Если в этом и заключается вся цель, то где я вижу необходимость в специализированной ветке. Это можно сделать, выбрав поток RAW из пула потоков системы и назначив ему нашу задачу (может быть, экземпляром нашего класса), и это все.
Итак, давайте подчинимся концепции ООП и напишем класс нужного нам типа. Есть много способов сделать что-то, правильное решение имеет значение.
Нам нужна задача, поэтому напишите определение задачи, которое можно запустить в потоке. Так что используйте Runnable.
Всегда помни implements
специально используется для передачи поведения и extends
используется для передачи функции / свойства.
Нам не нужно свойство потока, вместо этого мы хотим, чтобы наш класс вел себя как задача, которую можно запустить.
Runnable
это интерфейс, в то время как Thread
это класс, который реализует этот интерфейс. С точки зрения дизайна, должно быть четкое разделение между тем, как задача определена, и тем, как она выполняется. Первое является обязанностью Runnalbe
реализация, и последнее является работой Thread
учебный класс. В большинстве случаев реализация Runnable
это правильный путь, чтобы следовать.
Если вы используете runnable, вы можете сэкономить место для расширения до любого другого вашего класса.
Отделение класса Thread от реализации Runnable также позволяет избежать потенциальных проблем синхронизации между потоком и методом run(). Отдельный Runnable, как правило, дает большую гибкость в способах ссылки и выполнения исполняемого кода.
Это S SOLID: единственная ответственность.
Поток воплощает текущий контекст (как в контексте выполнения: кадр стека, идентификатор потока и т. Д.) Асинхронного выполнения фрагмента кода. Этот кусок кода в идеале должен быть одной и той же реализацией, будь то синхронный или асинхронный.
Если вы объедините их в одну реализацию, вы дадите результирующему объекту две несвязанные причины изменения:
- обработка потоков в вашем приложении (т.е. запрос и изменение контекста выполнения)
- Алгоритм, реализуемый фрагментом кода (выполняемая часть)
Если используемый вами язык поддерживает частичные классы или множественное наследование, то вы можете разделить каждую причину в своем собственном суперклассе, но он сводится к тому же, что и составление двух объектов, поскольку их наборы функций не перекрываются. Это для теории.
На практике, вообще говоря, программа не должна нести больше сложности, чем необходимо. Если у вас есть один поток, работающий над конкретной задачей, не меняя ее, вероятно, нет смысла делать задачи отдельными классами, и ваш код остается более простым.
В контексте Java, поскольку средство уже существует, вероятно, проще начать непосредственно с автономного Runnable
классы, и передать их экземпляры Thread
(или же Executor
) экземпляры. После того, как вы привыкли к этому шаблону, его не сложнее использовать (или даже читать), чем в случае простого запускаемого потока.
Одна из причин, по которой вы хотите реализовать интерфейс, а не расширять базовый класс, состоит в том, что вы уже расширяете какой-то другой класс. Вы можете расширить только один класс, но вы можете реализовать любое количество интерфейсов.
Если вы расширяете Thread, вы в основном препятствует выполнению вашей логики любым другим потоком, кроме 'this'. Если вы хотите, чтобы какой-то поток выполнял вашу логику, лучше просто реализовать Runnable.
Да, если вы вызываете вызов ThreadA, то нет необходимости вызывать метод start, а метод run вызывается только после вызова класса ThreadA. Но если использовать вызов ThreadB, то необходимо вызвать начальный поток для вызова метода run. Если вам нужна помощь, ответьте мне.
Я считаю, что наиболее полезно использовать Runnable по всем упомянутым причинам, но иногда мне нравится расширять Thread, чтобы я мог создать свой собственный метод остановки потока и вызывать его непосредственно в созданном мной потоке.
Java не поддерживает множественное наследование, поэтому, если вы расширяете класс Thread, никакой другой класс расширяться не будет.
Например: если вы создаете апплет, то он должен расширять класс Applet, поэтому здесь единственный способ создать поток - реализовать интерфейс Runnable.
Добавление двух моих центов здесь - Всегда, когда это возможно, используйте implements Runnable
, Ниже приведены два предупреждения о том, почему вы не должны использовать extends Thread
s
В идеале вы никогда не должны расширять класс Thread;
Thread
класс должен быть сделанfinal
, По крайней мере, его методы, такие какthread.getId()
, Смотрите это обсуждение для ошибки, связанной с расширениемThread
s.Те, кто любит разгадывать головоломки, могут увидеть еще один побочный эффект расширения темы. Приведенный ниже код напечатает недоступный код, когда никто не уведомляет их.
Пожалуйста, смотрите http://pastebin.com/BjKNNs2G.
public class WaitPuzzle {
public static void main(String[] args) throws InterruptedException {
DoNothing doNothing = new DoNothing();
new WaitForever(doNothing).start();
new WaitForever(doNothing).start();
new WaitForever(doNothing).start();
Thread.sleep(100);
doNothing.start();
while(true) {
Thread.sleep(10);
}
}
static class WaitForever extends Thread {
private DoNothing doNothing;
public WaitForever(DoNothing doNothing) {
this.doNothing = doNothing;
}
@Override
public void run() {
synchronized (doNothing) {
try {
doNothing.wait(); // will wait forever here as nobody notifies here
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Unreachable Code");
}
}
}
static class DoNothing extends Thread {
@Override
public void run() {
System.out.println("Do Nothing ");
}
}
}