С какого времени я провел с потоками в 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
Есть ли существенная разница в этих двух блоках кода?
Да: реализация Runnable
- это предпочтительный способ сделать это, IMO. Вы не специализируетесь на поведении потоков. Вы просто даете ему что-то бежать. Это означает composition - философский "более чистый" путь.
В практическом плане это означает, что вы можете реализовать Runnable
и перейти от другого класса.
tl; dr: реализует Runnable лучше. Тем не менее, оговорка важна
В общем, я бы рекомендовал использовать что-то вроде Runnable
, а не Thread
, потому что он позволяет вам поддерживать вашу работу только в сочетании с вашим выбором concurrency. Например, если вы используете Runnable
и позже решите, что на самом деле это не требует его собственного Thread
, вы можете просто вызвать threadA.run().
Предостережение: Вокруг здесь я решительно отвергаю использование необработанных потоков. Я предпочитаю использовать Callables и FutureTasks (из javadoc: "Отказоустойчивое асинхронное вычисление" ). Интеграция тайм-аутов, правильная отмена и объединение потоков современной поддержки concurrency для меня намного полезнее, чем груды необработанных потоков.
Последующее наблюдение: существует конструктор FutureTask
, который позволяет использовать Runnables (если это вам больше всего нравится) и до сих пор пользуются преимуществами современных инструментов concurrency. Чтобы процитировать javadoc:
Если вам не нужен конкретный результат, подумайте о том, чтобы использовать конструкции формы:
Future<?> f = new FutureTask<Object>(runnable, null)
Итак, если мы заменим их Runnable
на ваш threadA
, мы получим следующее:
new FutureTask<Object>(threadA, null)
Другим вариантом, который позволяет вам оставаться ближе к Runnables, является ThreadPoolExecutor. Вы можете использовать метод execute для передачи в Runnable для выполнения "данной задачи в будущем".
Если вы хотите попробовать использовать пул потоков, фрагмент кода выше будет выглядеть примерно следующим образом (используя Executors.newCachedThreadPool() factory):
ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());
es
было бы лучше в качестве статического (или внедренного) поля, поэтому оно создается только один раз.
Мораль истории:
Наследовать, только если вы хотите переопределить какое-либо поведение.
Или, скорее, его следует читать как:
Наследовать меньше, больше интерфейса.
run()
.
Ну так много хороших ответов, я хочу добавить еще об этом. Это поможет понять Extending v/s Implementing Thread
.
Extends очень тесно связывает два файла класса и может привести к довольно сложной работе с кодом.
Оба подхода выполняют ту же работу, но были некоторые различия.
Наиболее распространенное различие -
Однако одна существенная разница между реализацией 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 стал Thread, вам нужно создать экземпляр Thread и передать себя в качестве цели.
В большинстве случаев интерфейс Runnable должен использоваться, если вы планируете переопределить метод run()
и другие методы Thread. Это важно, потому что классы не должны подклассифицироваться, если программист не намерен изменять или улучшать фундаментальное поведение класса.
При необходимости расширения суперкласса реализация интерфейса Runnable более подходит, чем использование класса Thread. Потому что мы можем расширить другой класс, внедряя интерфейс Runnable для создания потока.
Надеюсь, это поможет!
ExtendsThread et = new ExtendsThread();
Thread tc1 = new Thread(et);
tc1.start();
Thread.sleep(1000);
Thread tc2 = new Thread(et);
tc2.start();
Thread.sleep(1000);
Thread tc3 = new Thread(et);
tc3.start();
Это понятнее?
Одна вещь, которую я удивляю, еще не упоминалась, заключается в том, что реализация Runnable
делает ваш класс более гибким.
Если вы расширяете поток, то действие, которое вы делаете, всегда будет в потоке. Однако, если вы реализуете Runnable
, это не обязательно. Вы можете запустить его в потоке или передать его какой-либо службе-исполнителю или просто передать его как задачу в однопоточном приложении (возможно, для запуска позднее, но в пределах одного потока). Опции намного более открытые, если вы просто используете Runnable
, чем если бы вы привязали себя к Thread
.
Thread
потому что Thread implements Runnable
… ;-) Но это приятнее делать это с Runnable
чем делать это с Thread
!
Thread
добавляет множество дополнительных вещей, которые вам не нужны, а во многих случаях не нужны. Вам всегда лучше реализовывать интерфейс, который соответствует тому, что вы на самом деле делаете.
Если вы хотите реализовать или расширить какой-либо другой класс, тогда Runnable
интерфейс наиболее предпочтителен другим, если вы не хотите, чтобы какой-либо другой класс расширялся или реализовывался, тогда класс Thread
предпочтительнее
Самое распространенное различие -
Когда вы extends Thread
класс, после этого вы не можете расширить любой другой класс, который вам нужен. (Как вы знаете, Java не позволяет наследовать более одного класса).
Когда вы implements Runnable
, вы можете сохранить пространство для своего класса для расширения любого другого класса в будущем или сейчас.
Java не поддерживает множественное наследование, а это значит, что вы можете расширять только один класс на Java, поэтому, как только вы расширили класс Thread, вы потеряли свой шанс и не можете расширять или наследовать другой класс на Java.
/li >В объектно-ориентированном программировании расширение класса обычно означает добавление новых функций, изменение или улучшение поведения. Если мы не вносим никаких изменений в Thread, используйте вместо этого интерфейс Runnable.
Runnable interface представляет собой задачу, которая может выполняться либо простым потоком, либо исполнителями, либо любыми другими способами. поэтому логическое разделение задачи как Runnable than Thread - хорошее дизайнерское решение.
Разделительная задача как Runnable означает, что мы можем повторно использовать задачу, а также иметь возможность выполнять ее с разных средств. так как вы не можете перезапустить Thread после его завершения. снова Runnable vs Thread для задачи, Runnable - победитель.
Java-дизайнер распознает это и то, почему исполнители принимают Runnable as Task, и у них есть рабочий поток, который выполняет эту задачу.
Наследование всех методов Thread - это дополнительные накладные расходы только для представления задачи, которую можно легко выполнить с помощью Runnable.
Предоставлено javarevisited.blogspot.com
Это были некоторые заметные различия между Thread и Runnable в Java, если вы знаете какие-либо другие отличия от Thread vs Runnable, чем поделиться им с помощью комментариев. Я лично использую Runnable over Thread для этого сценария и рекомендует использовать интерфейс Runnable или Callable на основе вашего требования.
Однако значительная разница.
Когда вы extends Thread
класс, каждый из ваших потоков создает уникальный объект и связывается с ним.
Когда вы implements Runnable
, он разделяет один и тот же объект на несколько потоков.
Собственно, нецелесообразно сравнивать Runnable
и Thread
друг с другом.
У этих двух есть зависимость и взаимосвязь в многопоточности, как и отношение Wheel and Engine
транспортного средства.
Я бы сказал, есть только один способ многопоточности с двумя шагами. Позвольте мне высказать свою мысль.
Runnable:
При реализации interface Runnable
это означает, что вы создаете нечто, что run able
в другом потоке. Теперь создание чего-то, что может выполняться внутри потока (runnable внутри потока), не означает создание потока.
Таким образом, класс MyRunnable
представляет собой не что иное, как обычный класс с методом void run
.
И это объекты будут обычными объектами только с методом run
, который будет нормально выполняться при вызове. (если мы не передадим объект в потоке).
Тема: class Thread
, я бы сказал, очень специальный класс с возможностью запуска нового потока, который фактически позволяет многопоточность через метод start()
.
Почему не разумно сравнивать?
Потому что нам нужны оба для многопоточности.
Для многопоточности нам нужны две вещи:
Таким образом, технически и теоретически, оба из них необходимы для запуска потока, один из них будет запускать, а один будет запустит его (как Wheel and Engine
сильным автомобилем).
Чтобы вы не могли начать поток с MyRunnable
, вам нужно передать его в экземпляр Thread
.
Но можно создать и запустить поток только с помощью class Thread
, потому что Class Thread
реализует Runnable
, поэтому мы все знаем, что Thread
также является Runnable
внутри.
Наконец Thread
и Runnable
дополняют друг друга для многопоточности, а не для конкурента или замены.
ThreadA
больше не имеет смысла
Вы должны реализовать 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
over Thread
, таких как единая ответственность и использование композиции, потому что вы не специализируетесь на потоке поведение. Этот способ также позволяет избежать создания дополнительного класса, если все, что вам нужно, это Runnable
, как и в ваших примерах.
() -> {}
должен представлять пользовательскую логику, которая кому-то нужна? Так что было бы лучше сказать как () -> { /* Code here */ }
?
Создание интерфейса дает более четкое разделение между вашим кодом и реализацией потоков, поэтому я предпочел бы реализовать Runnable в этом случае.
Thread
, вы потеряли свой шанс и не можете продлить или наследовать другой класс на Java.Thread
, а вместо этого используем интерфейс Runnable
.
Интерфейс Runnable
представляет собой Task
, который может выполняться либо обычным Thread
, либо Executors
или любым другим способом. Поэтому логическое разделение Task
как Runnable
, чем Thread
является хорошим конструктивным решением.Runnable
означает, что мы можем повторно использовать задачу, а также иметь возможность выполнять ее с разных средств. Поскольку вы не можете перезапустить Thread
после завершения, снова Runnable
vs Thread
для задачи, Runnable
является победителем.Executors
принимает Runnable
как Task
, и у них есть рабочий поток, который выполняет эту задачу.Thread
- дополнительные накладные расходы только для представления Task
, который можно легко выполнить с помощью Runnable
.Поскольку это очень популярная тема, и хорошие ответы распространяются повсюду и рассматриваются с большой глубиной, я счел оправданным составить хорошие ответы от других в более сжатые формы, так что новички имеют простой обзор авансовые:
Обычно вы добавляете класс для добавления или изменения функциональности. Итак, если вы не хотите перезаписывать любое поведение потока, используйте Runnable.
В том же свете, если вам не нужны методы наследовать, вы можете обойтись без служебных с помощью Runnable.
Одиночное наследование. Если вы расширяете тему Thread, вы не можете распространяться ни на один другой класс, поэтому, если это то, что вам нужно сделать, вам нужно использовать Runnable.
Хороший дизайн для разделения логики домена с помощью технических средств, в этом смысле лучше иметь задачу Runnable изолировать вашу задачу от вашего > бегун.
Вы можете выполнить один и тот же Runnable объект несколько раз, но объект Thread может быть запущен только один раз. (Может быть, причина, почему исполнители действительно принимают Runnables, но не Threads.)
Если вы разрабатываете свою задачу как Runnable, у вас есть гибкость, как использовать ее сейчас и в будущем. Вы можете запустить его одновременно с помощью Executors, но также через Thread. И вы все равно можете использовать/вызывать его не одновременно в пределах одного потока, как и любой другой обычный тип/объект.
Это облегчает разделить задачу-логику и concurrency аспекты ваших модульных тестов.
Если вас интересует этот вопрос, вас также может заинтересовать разница между Callable и Runnable.
Кажется, что все считают, что внедрение Runnable - это способ пойти, и я не согласен с ними, но есть также аргумент в пользу расширения Thread, по моему мнению, на самом деле вы его продемонстрировали в своем коде.
Если вы реализуете Runnable, тогда класс, который реализует Runnable, не имеет контроля над именем потока, это вызывающий код, который может установить имя потока, например:
new Thread(myRunnable,"WhateverNameiFeelLike");
но если вы продолжите Thread, тогда вы сможете управлять этим в самом классе (как и в вашем примере, вы называете поток ThreadB). В этом случае вы:
A) может дать ему более полезное имя для целей отладки
B) заставляют использовать это имя для всех экземпляров этого класса (если вы не игнорируете тот факт, что это поток, и делайте с ним выше, как если бы он был Runnable, но мы говорим о соглашении здесь в любом поэтому я могу игнорировать эту возможность).
Вы можете, например, взять трассировку стека из своего создания и использовать это как имя потока. Это может показаться странным, но в зависимости от того, как структурирован ваш код, он может быть очень полезен для целей отладки.
Это может показаться маленькой, но там, где у вас очень сложное приложение с большим количеством потоков, и внезапно все "остановилось" (либо по причинам тупика, либо, возможно, из-за недостатка в сетевом протоколе, который было бы менее очевидным - или другими бесконечными причинами), тогда получение дампа стека из Java, где все потоки называются "Thread-1", "Thread-2", "Thread-3", не всегда очень полезно (это зависит от того, как ваши потоки структурированы и можете ли вы с пользой сказать, что именно по их трассировке стека - не всегда возможно, если вы используете группы из нескольких потоков, все из которых работают с одним и тем же кодом).
Сказав, что вы, конечно же, можете сделать это в общем виде, создав расширение класса потока, которое устанавливает его имя в трассировку стека своего вызова создания, а затем использует его с вашими реализациями Runnable вместо стандартного java класс Thread (см. ниже), но в дополнение к трассировке стека может быть больше информации, специфичной для контекста, которая была бы полезной в имени потока для отладки (ссылка на одну из многих очередей или сокетов, которые она могла бы обрабатывать, например, в этом случае вы может предпочесть продлить 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]
Thread.currentThread().setName("WhateverNameiFeelLike");
Runnable, потому что:
Даже если вам это не нужно сейчас, вы можете в будущем. Поскольку нет преимущества для переопределения Thread, Runnable - лучшее решение.
Это обсуждается в учебном пособии Oracle Определение и запуск потока:
Какую из этих идиом вы должны использовать? Первая идиома, в которой используется Runnable object, является более общим, поскольку объект Runnable может подкласс - класс, отличный от Thread. Вторая идиома проще в использовании в простых приложениях, но ограничивается тем, что ваша задача класс должен быть потомком Thread. Этот урок фокусируется на первом подход, который отделяет задачу Runnable от объекта Thread который выполняет задачу. Этот подход не только более гибкий, но он применим к API-интерфейсам управления потоками высокого уровня позже.
Другими словами, реализация Runnable
будет работать в сценариях, где ваш класс расширяет класс, отличный от Thread
. Java не поддерживает множественное наследование. Кроме того, расширение Thread
будет невозможно при использовании некоторых высокоуровневых API управления потоками. Единственный сценарий, в котором расширение Thread
предпочтительнее, - это небольшое приложение, которое в будущем не будет обновляться. Практически лучше реализовать Runnable
, поскольку он более гибкий по мере роста вашего проекта. Изменение дизайна не окажет большого влияния, поскольку вы можете реализовать множество интерфейсов в java, но только расширяете один класс.
Разница между расширением потока и реализацией Runnable:
Если я не ошибаюсь, он более или менее похож на
В чем разница между интерфейсом и абстрактным классом?
extends устанавливает " Is A" отношение и интерфейс обеспечивает " имеет".
Предпочитает реализует Runnable:
Предпочитаете " расширять поток":
Как правило, вам не нужно переопределять поведение Thread. Таким образом, реализует Runnable является предпочтительным в большинстве случаев.
С другой стороны, использование расширенного API ExecutorService
или ThreadPoolExecutorService
обеспечивает большую гибкость и контроль.
Взгляните на этот вопрос СЕ:
Можем ли мы повторно посетить основную причину, по которой мы хотели, чтобы наш класс вел себя как Thread
?
Нет никакой причины, мы просто хотели выполнить задачу, скорее всего, в асинхронном режиме, что точно означает, что выполнение задачи должно входить из нашего основного потока и основного потока, если заканчивается раньше, может или не может ждать для разветвленного пути (задачи).
Если это целая цель, то где я вижу потребность в специализированном потоке. Это может быть достигнуто путем сбора RAW-потока из пула системных потоков и назначения его нашей задачи (может быть экземпляром нашего класса), и это все.
Итак, давайте повинуемся концепции ООП и напишем класс типа, который нам нужен. Есть много способов сделать что-то, делайте это правильно.
Нам нужна задача, поэтому напишите определение задачи, которое можно запустить в потоке. Поэтому используйте Runnable.
Всегда помнить implements
специально используется для придания поведения, а extends
используется для передачи функции/свойства.
Нам не нужно свойство потока, вместо этого мы хотим, чтобы наш класс вел себя как задача, которая может быть запущена.
если вы используете runnable, вы можете сохранить пространство, чтобы перейти к любому другому классу.
Одной из причин, по которой вы хотите реализовать интерфейс, а не расширять базовый класс, является то, что вы уже расширяете какой-либо другой класс. Вы можете расширить только один класс, но вы можете реализовать любое количество интерфейсов.
Если вы расширяете Thread, вы в основном препятствуете выполнению вашей логики любым другим потоком, чем 'this'. Если вы хотите, чтобы какой-либо поток выполнял вашу логику, лучше просто выполнить Runnable.
Отделить класс Thread от реализации Runnable также позволяет избежать возможных проблем синхронизации между потоком и методом run(). Отдельный Runnable обычно обеспечивает большую гибкость в том, как ссылающийся и исполняемый код ссылается и выполняется.
Лучший способ для большинства рабочих потоков - полностью потопить потоки в рабочем классе, чтобы ничто не мешало извне и вызывало нежелательные и недопустимые состояния потоков/классов.
Я только что опубликовал пример, поэтому я также поделюсь этим с вами:
/**
* This worker can only run once
* @author JayC667
*/
public class ProperThreading {
private final Thread mThread = new Thread(() -> runWorkingLoop()); // if you want worker to be able to run multiple times, move initialisation into startThread()
private volatile boolean mThreadStarted = false;
private volatile boolean mStopRequested = false;
private final long mLoopSleepTime;
public ProperThreading(final long pLoopSleepTime /* pass more arguments here, store in members */ ) {
mLoopSleepTime = pLoopSleepTime;
}
public synchronized void startThread() {
if (mThreadStarted) throw new IllegalStateException("Worker Thread may only be started once and is already running!");
mThreadStarted = true;
mThread.start();
}
private void runWorkingLoop() {
while (!mStopRequested /* && other checks */ ) {
try {
// do the magic work here
Thread.sleep(mLoopSleepTime);
} catch (final InterruptedException e) {
break;
} catch (final Exception e) {
// do at least some basic handling here, you should NEVER ignore exception unless you know exactly what you're doing, and then it should be commented!
}
}
}
public synchronized void stopThread() {
if (!mThreadStarted) throw new IllegalStateException("Worker Thread is not even running yet!");
mStopRequested = true;
mThread.interrupt();
}
}
Простейшим объяснением будет реализация Runnable
, которую мы можем назначить одному и тому же объекту нескольким потокам, и каждый Thread
имеет общие состояния и поведение объектов.
Например, предположим, что существует два потока, thread1 помещает целое число в массив, а thread2 принимает целые числа из массива, когда массив заполняется. Обратите внимание: для того, чтобы thread2 работал, он должен знать состояние массива, независимо от того, заполнил ли он thread1 или нет.
Реализация Runnable
позволяет использовать эту гибкость для совместного использования объекта, тогда как extends Thread
позволяет создавать новые объекты для каждого потока, поэтому любое обновление, которое выполняется thread1, теряется для thread2.
Единственное отличие между реализацией Runnable и продолжением Thread заключается в том, что, расширяя Thread, каждый из ваших потоков имеет уникальный объект, связанный с ним, тогда как реализация Runnable, многие потоки могут совместно использовать один и тот же экземпляр объекта.
Класс, реализующий Runnable, не является потоком и просто классом. Чтобы Runnable выполнялся потоком, вам нужно создать экземпляр Thread и передать экземпляр Runnable в качестве цели.
В большинстве случаев интерфейс Runnable должен использоваться, если вы планируете переопределять метод run() и другие методы Thread. Это важно, потому что классы не должны подклассифицироваться, если программист не намерен изменять или улучшать фундаментальное поведение класса.
При необходимости расширения суперкласса реализация интерфейса Runnable более подходит, чем использование класса Thread. Потому что мы можем расширить другой класс, внедряя интерфейс Runnable для создания потока. Но если мы просто расширяем класс Thread, мы не можем наследовать ни от какого другого класса.
Добавление моих двух центов здесь -
Всегда, когда это возможно, используйте implements Runnable
. Ниже приведены два оговорки о том, почему вы не должны использовать
extends Thread
s
В идеале вы никогда не должны расширять класс Thread; класс Thread
должен быть сделан final
.
По крайней мере, его методы вроде thread.getId()
.
См. это обсуждение ошибки, связанной с расширением Thread
s.
Те, кто любит решать головоломки, могут видеть другой побочный эффект расширения Thread. Код ниже будет печатать недостижимый код, когда никто не уведомляет их.
См. 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 ");
}
}
}
Разница между потоком и runnable . Если мы создаем Thread с использованием класса Thread, тогда количество потоков равно количеству созданного нами объекта. Если мы создаем поток, реализуя интерфейс runnable, мы можем использовать один объект для создания нескольких потоков. Один объект разделяется несколькими Thread.So он будет потреблять меньше памяти
Таким образом, в зависимости от требования, если наши данные не являются senstive. Таким образом, он может быть разделен между несколькими Thread, которые мы можем использовать интерфейс Runnable.
Что S SOLID Единая ответственность.
Нить воплощает выполняемый контекст (как в контексте выполнения: стек стека, идентификатор потока и т.д.) асинхронного выполнения части кода. Эта часть кода идеально должна быть одной и той же реализацией, будь то синхронной или асинхронной.
Если вы объединяете их вместе в одну реализацию, вы даете результирующему объекту две несвязанные причины изменения:
Если язык, который вы используете, поддерживает частичные классы или множественное наследование, вы можете разделить каждую причину в своем собственном суперклассе, но это сводится к тому же, что и для создания двух объектов, поскольку их набор функций не перекрывается. Это для теории.
На практике, вообще говоря, программа не нуждается в большей сложности, чем необходимо. Если у вас есть один поток, работающий над конкретной задачей, без изменения этой задачи, вероятно, нет смысла делать задачи отдельными классами, а ваш код будет проще.
В контексте Java, поскольку средство уже существует, возможно, проще начать прямо с автономных классов Runnable
и передать свои экземпляры в Thread
(или ). После того, как он используется для этого шаблона, его труднее использовать (или даже читать), чем простой исполняемый поток.
Java не поддерживает множественную наследование, поэтому, если вы расширяете класс Thread, тогда никакой другой класс не будет расширен.
Например: если вы создаете апплет, то он должен расширять класс Applet, поэтому здесь единственный способ создать поток - реализовать интерфейс Runnable
Я считаю, что наиболее полезно использовать Runnable для всех упомянутых причин, но иногда мне нравится продлить Thread, поэтому я могу создать свой собственный метод остановки потока и вызвать его непосредственно на созданном мной потоке.
Да, Если вы вызываете вызов ThreadA, тогда не нужно вызывать метод start, а метод run - это вызов после вызова класса ThreadA. Но если использовать ThreadB-вызов, тогда необходимо создать начальный поток для метода вызова. Если у вас есть дополнительная помощь, ответьте мне.
Поведение содержит теги, которые не предназначены для доступа;
однако, если вы, суб-класс Thread, должны рассмотреть возможность использования Thread.
public class ThreadMain {
public int getId() {
return 12345678;
}
public String getName() {
return "Hello World";
}
public String getState() {
return "testing";
}
public void example() {
new Thread() {
@Override
public void run() {
System.out.println("id: "+getId()+", name: "+getName()+", state: "+getState());
}
}.start();
}
public static void main(String[] args) {
new ThreadMain().example();
}
}
Если вы запустите это, вы можете ожидать
id: 12345678, name: Hello World, state: testing
однако вы не называете методы, которые, по вашему мнению, являются, потому что вы используете метод в Thread
not ThreadMain
, и вместо этого вы видите что-то вроде
id: 11, name: Thread-0, state: RUNNABLE
Runnable
- это интерфейс, а Thread
- класс, реализующий этот интерфейс. С точки зрения дизайна, должно быть чистое разделение между тем, как определяется задача и между тем, как она выполняется. Первый отвечает за реализацию Runnalbe
, а последняя - в классе Thread
. В большинстве случаев реализация Runnable
- правильный путь.
Расширяя класс потока, производный класс не может расширять любой другой базовый класс, потому что java допускает только одно наследование. напротив, путем реализации интерфейса runnable класс по-прежнему распространяет другой базовый класс.
Ниже приведено наиболее существенное различие между реализацией Runnable и продолжением Thread:
Расширяя Thread, производный класс сам по себе является объектом потока, тогда как интерфейс реализации Runnable имеет один и тот же объект для нескольких потоков.
В редком случае вы запускаете его только один раз, вы должны продлить Thread из-за DRY. Если вы вызываете его несколько раз, вы должны реализовать Runnable, потому что тот же поток не следует перезапускать.
Это может быть не ответ, но в любом случае; есть еще один способ создания потоков:
Thread t = new Thread() {
public void run() {
// Code here
}
}
Класс Thread определяет несколько методов, которые могут быть overriden
расширенным классом. Но для создания потока мы должны переопределить метод run()
. То же самое относится и к Runnable
.
Однако Runnable
является предпочтительным способом создания потока. Основные причины:
Так как Runnable - это интерфейс, вы можете расширить другие классы. Но если вы продляете Thread, то этот вариант не будет.
Если вы не изменяете или не улучшаете множество функций Thread
, и расширение класса Thread
не является предпочтительным.
Простой способ сказать: Если вы реализуете интерфейс, который означает, что вы реализуете все его методы, и если вы расширяете класс, вы наследуете метод по вашему выбору... В этом случае существует только один метод с именем Run(), поэтому лучше реализовать интерфейс Runnable.
Основное различие между Thread и Runnable: - Thread is like: Worker (выполнить Runnable) - Runnable - это как: Job (для выполнения Thread)
Я бы сказал, что фактическая задача отделена от потока. Мы можем передать задачу в Thread, Executor framework и т.д. В случае Runnable, тогда как с расширением задачи Thread связано с самим объектом потока. Изоляция задачи не может быть выполнена в случае расширения Thread. Это похоже на то, что мы сжигаем задачу для объекта Thread только что-то вроде IC-чипа (и, более конкретно, не будем обрабатывать задачу).
Вы можете использовать их совместно
Пример:
public class A implements Runnable{
@Override
public void run() {
while(true){
System.out.println("Class A is running");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}}
}
}
public class Test {
public static void main(String[] args) {
Thread myThread =new Thread(new A());// 1
myThread.start();
System.out.println(" executed after thread A");//will never be reached
}
}
new Thread(new A())
и вызвать метод start()
вместо run()
.
interrupt()
. Опять же, это идея, она может быть полезна в правильном случае, однако я не рекомендую ее.