Из того, сколько времени я провел с потоками в 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
Есть ли существенная разница в этих двух блоках кода?
java
multithreading
runnable
implements
java-threads
user65374
источник
источник
interrupt()
. Опять же, это идея, это может быть полезно в правильном случае, однако я не рекомендую это.Ответы:
Да
Runnable
, ИМО - предпочтительный способ сделать это. Вы на самом деле не специализируете поведение потока. Вы просто даете ему что-то, чтобы бежать. Это означает, что композиция - это философски более чистый путь.В практическом плане это означает, что вы можете реализовывать
Runnable
и расширять другие классы.источник
if (numberCores > 4) myExecutor.excute(myRunnable); else myRunnable.run()
extends Thread
и, если вы не хотите многопоточности, зачем вам вообще это реализовыватьRunnable
...tl; dr: реализует Runnable лучше. Тем не менее, предостережение важно
В общем, я бы рекомендовал использовать что-то подобное,
Runnable
а неThread
потому, что это позволяет вам сохранить свою работу только слабо связанной с вашим выбором параллелизма. Например, если вы используете aRunnable
и позже решите, что это на самом деле не требует своегоThread
, вы можете просто вызвать threadA.run ().Предостережение: где-то здесь я настоятельно не рекомендую использовать сырые темы. Я очень предпочитаю использовать Callables и FutureTasks (из javadoc: « отменяемые асинхронные вычисления»). Интеграция тайм-аутов, правильная отмена и объединение потоков в современной поддержке параллелизма гораздо более полезны для меня, чем груды необработанных потоков.
Продолжение: есть
FutureTask
конструктор, который позволяет вам использовать Runnables (если это то, с чем вам удобнее всего) и при этом пользоваться преимуществами современных инструментов параллелизма. Процитирую Javadoc :Если вам не нужен конкретный результат, рассмотрите возможность использования конструкций вида:
Итак, если мы заменим их
runnable
на вашиthreadA
, мы получим следующее:Другой вариант, который позволяет вам оставаться ближе к Runnables, - ThreadPoolExecutor . Вы можете использовать метод execute для передачи Runnable для выполнения «заданной задачи когда-нибудь в будущем».
Если вы хотите попробовать использовать пул потоков, приведенный выше фрагмент кода будет выглядеть примерно так (с использованием фабричного метода Executors.newCachedThreadPool () ):
источник
es
было бы лучше в качестве статического (или внедренного) поля, чтобы оно создавалось только один раз.FutureTask
напрямую - это совсем не то, что вы хотите сделать.ExecutorService
s создаст соответствующуюFuture
для вас , когда вы / им. Аналогично для с и когда вы / .submit
Runnable
Callable
ScheduledExecutorService
ScheduledFuture
schedule
Runnable
Callable
Мораль истории:
Наследовать, только если вы хотите переопределить какое-либо поведение.
Или, скорее, это следует читать как:
Унаследуй меньше, интерфейс больше.
источник
run()
метода.java.lang.Thread
, переопределивrun()
метод. В этом случае вам нужно переопределитьstart()
метод, я думаю. Обычно вы просто повторно используете поведениеjava.lang.Thread
, вводя свой блок выполнения в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.
Следующий пример поможет вам лучше понять
Вывод вышеуказанной программы.
В подходе интерфейса Runnable создается только один экземпляр класса, и он используется разными потоками. Таким образом, значение счетчика увеличивается для каждого доступа к потоку.
Принимая во внимание, что подход класса Thread, вы должны создать отдельный экземпляр для каждого доступа потока. Следовательно, для каждого экземпляра класса выделяется разная память, и у каждого есть отдельный счетчик, значение остается тем же, что означает, что никакого приращения не произойдет, потому что ни одна из ссылок на объект не является одинаковой.
Когда использовать Runnable?
Используйте интерфейс Runnable, когда вы хотите получить доступ к тем же ресурсам из группы потоков. Избегайте использования класса Thread здесь, потому что создание нескольких объектов потребляет больше памяти, и это приводит к значительному снижению производительности.
Класс, который реализует Runnable, не является потоком, а просто классом. Чтобы Runnable стал потоком, вам нужно создать экземпляр потока и передать себя в качестве цели.
В большинстве случаев интерфейс 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();
это яснее?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
неверным? Если нет, то каков случай, который демонстрирует это?Одна вещь, которую я удивляюсь, еще не упоминалась, это то, что реализация
Runnable
делает ваш класс более гибким.Если вы расширяете поток, то действие, которое вы делаете, всегда будет в потоке. Тем не менее, если вы реализуете
Runnable
это не должно быть. Вы можете запустить его в потоке, или передать его какому-либо сервису-исполнителю, или просто передать как задачу в рамках одного многопоточного приложения (возможно, для запуска в более позднее время, но в том же потоке). Варианты намного более открыты, если вы просто используете,Runnable
чем если вы привязываете себяThread
.источник
Thread
объектом, потому чтоThread implements Runnable
… ;-) Но это приятнее делать это с a,Runnable
чем делать с aThread
!Thread
добавляет много лишних вещей, которые вам не нужны, а во многих случаях не нужны. Вам всегда лучше реализовывать интерфейс, который соответствует тому, что вы на самом деле делаете.Если вы хотите реализовать или
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 over Thread для этого сценария и рекомендую использовать интерфейс Runnable или Callable в зависимости от ваших требований.
Однако существенная разница есть.
Когда вы
extends Thread
занимаетесь классом, каждый ваш поток создает уникальный объект и связывается с ним. Когда выimplements Runnable
, он разделяет один и тот же объект на несколько потоков.источник
На самом деле, не стоит сравнивать
Runnable
иThread
друг с другом.Эти два имеют зависимость и отношения в многопоточности так же, как
Wheel and Engine
отношения автомобиля.Я бы сказал, что есть только один способ многопоточности с двумя шагами. Позвольте мне высказать свое мнение.
Runnable:
при реализации
interface Runnable
это означает, что вы создаете что-то, что находитсяrun able
в другом потоке. Теперь создание чего-либо, что может выполняться внутри потока (может выполняться внутри потока), не означает создание потока.Таким образом, класс
MyRunnable
- это не что иное, как обычный класс сvoid run
методом. И это объекты будут обычными объектами только с методом,run
который будет нормально выполняться при вызове. (если мы не передадим объект в потоке).Thread:
class Thread
я бы сказал, очень специальный класс с возможностью запуска нового потока, который фактически позволяет многопоточность через егоstart()
метод.Почему не стоит сравнивать?
Потому что нам нужны они оба для многопоточности.
Для многопоточности нам нужны две вещи:
Так что технически и теоретически оба они необходимы для запуска потока, один будет запущен, а другой заставит его работать (как
Wheel and Engine
в автомобиле).Вот почему вы не можете запустить поток,
MyRunnable
вам нужно передать его экземпляруThread
.Но можно создать и запустить поток только с использованием,
class Thread
потому что ClassThread
реализует,Runnable
так что мы все знаем,Thread
также являетсяRunnable
внутренним.Наконец
Thread
иRunnable
дополняют друг друга для многопоточности не конкурента или замены.источник
ThreadA
больше не имеет смыслаВы должны реализовать Runnable, но если вы работаете на Java 5 или выше, вы не должны запускать его с,
new Thread
а использовать вместо этого ExecutorService . Подробнее см .: Как реализовать простую многопоточность в Java .источник
Я не эксперт, но я могу придумать одну причину для реализации Runnable вместо расширения Thread: Java поддерживает только одиночное наследование, поэтому вы можете расширять только один класс.
Редактировать: первоначально говорилось: «Реализация интерфейса требует меньше ресурсов». также, но вам нужно создать новый экземпляр Thread, так что это было неправильно.
источник
Я бы сказал, что есть третий путь:
Возможно, на это немного повлияло мое недавнее интенсивное использование Javascript и Actionscript 3, но в этом случае вашему классу не нужно реализовывать довольно расплывчатый интерфейс, как
Runnable
.источник
С выпуском Java 8 появился третий вариант.
Runnable
это функциональным интерфейсом , что означает, что его экземпляры могут быть созданы с помощью лямбда-выражений или ссылок на методы.Ваш пример можно заменить на:
или если вы хотите использовать
ExecutorService
и ссылку на метод:Они не только намного короче ваших примеров, но также обладают многими преимуществами, указанными в других ответах на использование
Runnable
overThread
, такими как одиночная ответственность и использование композиции, потому что вы не специализируете поведение потока. Этот способ также позволяет избежать создания дополнительного класса, если все, что вам нужно, это то,Runnable
что вы делаете в своих примерах.источник
() -> {}
должен представлять собой пользовательскую логику, которая кому-то нужна? Так было бы лучше сказать как() -> { /* Code here */ }
?Создание интерфейса дает более четкое разделение между вашим кодом и реализацией потоков, поэтому я бы предпочел реализовать Runnable в этом случае.
источник
Кажется, все здесь думают, что реализация Runnable - это путь, и я на самом деле не согласен с ними, но, на мой взгляд, есть также причина для расширения Thread, фактически вы как бы продемонстрировали это в своем коде.
Если вы реализуете Runnable, то класс, реализующий Runnable, не будет контролировать имя потока, это вызывающий код, который может установить имя потока, например, так:
но если вы расширяете Thread, тогда вы можете управлять этим внутри самого класса (как в вашем примере вы называете поток «ThreadB»). В этом случае вы:
А) может дать ему более полезное имя для целей отладки
B) принуждают использовать это имя для всех экземпляров этого класса (если вы не игнорируете тот факт, что он является потоком, и делаете вышеизложенное с ним, как будто это Runnable, но мы говорим о соглашении здесь в любом случае, так что можно игнорировать эту возможность я чувствую).
Вы можете даже, например, взять трассировку стека его создания и использовать его в качестве имени потока. Это может показаться странным, но в зависимости от структуры вашего кода это может быть очень полезно для целей отладки.
Это может показаться мелочью, но когда у вас очень сложное приложение с большим количеством потоков и все внезапно «остановилось» (либо по причине тупиковой ситуации, либо, возможно, из-за недостатка сетевого протокола, который был бы меньше очевидно - или по другим бесконечным причинам) затем получение дампа стека из Java, где все потоки называются «Thread-1», «Thread-2», «Thread-3», не всегда очень полезно (это зависит от того, как ваши потоки структурирован и можете ли вы с пользой определить, что есть что, просто по их трассировке стека - не всегда возможно, если вы используете группы из нескольких потоков, все выполняющие один и тот же код).
Сказав, что вы, конечно, также можете сделать вышеупомянутое в общем виде, создав расширение класса потока, которое устанавливает его имя в виде трассировки стека его вызова создания, а затем используйте это с вашими реализациями Runnable вместо стандартного класса потока Java. (см. ниже), но в дополнение к трассировке стека может быть более специфичная для контекста информация, которая будет полезна в имени потока для отладки (ссылка на одну из множества очередей или сокетов, которые он может обработать, например, в этом случае вы можете предпочесть расширить Thread специально для этого случая, чтобы вы могли заставить компилятор заставить вас (или других пользователей, использующих ваши библиотеки) передавать определенную информацию (например, нужную очередь / сокет) для использования в имени).
Вот пример общего потока с трассировкой вызывающего стека в качестве имени:
и вот пример выходных данных, сравнивающих два имени:
источник
Runnable
действительно может управлять именем потока, поскольку поток, выполняющий код, по определению является текущим потоком (и любой код, который проходит проверки безопасности, имеет контроль над именами потоков). Учитывая, что вы посвятили половину своего поста «боже, как насчет названий тем!», Это кажется чем-то большим.Runnable потому что:
Даже если вам не нужно ничего этого сейчас, вы можете в будущем. Поскольку переопределение Thread не дает никаких преимуществ, Runnable является лучшим решением.
источник
Поскольку это очень популярная тема, и хорошие ответы распространяются повсеместно и рассматриваются очень глубоко, я чувствовал, что было бы оправданным объединить хорошие ответы от других в более сжатую форму, чтобы у новичков был простой обзор:
Вы обычно расширяете класс для добавления или изменения функциональности. Таким образом, если вы не хотите , чтобы перезаписать любое поведение темы , а затем использовать Runnable.
В том же свете, если вам не нужно , чтобы наследуют методы потоков, можно обойтись и без этого над головой , используя Runnable.
Одиночное наследование : если вы расширяете Thread, вы не можете расширяться из любого другого класса, поэтому, если это то, что вам нужно сделать, вы должны использовать Runnable.
Это хорошая идея, чтобы отделить доменную логику от технических средств, в этом смысле лучше иметь выполняемую задачу, изолирующую вашу задачу от вашего бегуна .
Вы можете выполнить один и тот же объект Runnable несколько раз , однако объект Thread можно запустить только один раз. (Возможно, причина, по которой Исполнители принимают Runnables, но не Threads.)
Если вы разрабатываете свою задачу как Runnable, у вас есть все возможности использовать ее сейчас и в будущем . Вы можете запустить его одновременно через Executors, а также через Thread. И вы все равно можете использовать / вызывать его не одновременно в том же потоке, как любой другой обычный тип / объект.
Это также облегчает разделение задач-логики и аспектов параллелизма в ваших модульных тестах .
Если вас интересует этот вопрос, вас также может заинтересовать разница между Callable и Runnable .
источник
Разница между расширением потока и реализацией Runnable:
источник
Это обсуждается в руководстве Oracle « Определение и запуск потока»:
Другими словами, реализация
Runnable
будет работать в сценариях, где ваш класс расширяет класс, отличный отThread
. Java не поддерживает множественное наследование. Кроме того, расширениеThread
будет невозможно при использовании некоторых API-интерфейсов управления потоками высокого уровня. Единственный сценарий, в котором расширениеThread
является предпочтительным, - это небольшое приложение, которое не будет обновляться в будущем. Это почти всегда лучше реализовать, такRunnable
как он становится более гибким по мере роста вашего проекта. Изменение дизайна не будет иметь большого влияния, так как вы можете реализовать много интерфейсов в Java, но расширить только один класс.источник
Самое простое объяснение состоит в том, что
Runnable
мы можем назначить один и тот же объект нескольким потокам, и каждый из нихThread
имеет одинаковые состояния и поведение объекта.Например, предположим, что есть два потока, thread1 помещает целое число в массив, а thread2 берет целые числа из массива, когда массив заполняется. Обратите внимание, что для работы thread2 необходимо знать состояние массива, заполнил ли thread1 или нет.
Реализация
Runnable
позволяет вам иметь эту гибкость для совместного использования объекта, тогда какextends Thread
позволяет создавать новые объекты для каждого потока, поэтому любое обновление, которое выполняется потоком 1, теряется потоком 2.источник
Если я не ошибаюсь, это более или менее похоже на
В чем разница между интерфейсом и абстрактным классом?
расширяет, устанавливает отношение « Is A », а интерфейс предоставляет возможность « имеет ».
Предпочитаю реализовать Runnable :
Предпочитаю " расширяет тему ":
Как правило, вам не нужно переопределять поведение потока. Так реализует Runnable предпочтительнее для большинства случаев.
С другой стороны, использование Advanced
ExecutorService
илиThreadPoolExecutorService
API обеспечивает большую гибкость и контроль.Посмотрите на этот вопрос SE:
ExecutorService vs Casual Spawner
источник
Отделение класса Thread от реализации Runnable также позволяет избежать потенциальных проблем синхронизации между потоком и методом run (). Отдельный Runnable, как правило, дает большую гибкость в способах ссылки и выполнения исполняемого кода.
источник
Это S из SOLID : Single ответственность.
Нить воплощает в себе текущий контекст (как в контексте выполнения: стек кадра, идентификатор потока и т.д.) от асинхронного выполнения куска кода. Этот кусок кода в идеале должен быть одной и той же реализацией, будь то синхронный или асинхронный .
Если вы объедините их в одну реализацию, вы дадите получившемуся объекту две несвязанные причины изменения:
Если используемый вами язык поддерживает частичные классы или множественное наследование, то вы можете отделить каждую причину в своем собственном суперклассе, но он сводится к тому же, что и составление двух объектов, поскольку их наборы функций не перекрываются. Это для теории.
На практике, вообще говоря, программа не должна нести больше сложности, чем необходимо. Если у вас есть один поток, работающий над конкретной задачей, не меняя ее, вероятно, нет смысла делать задачи отдельными классами, и ваш код остается более простым.
В контексте Java , поскольку средство уже существует , вероятно, проще начать непосредственно с автономных
Runnable
классов и передавать их экземплярыThread
(илиExecutor
) экземплярам. После того, как вы привыкли к этому шаблону, его не сложнее использовать (или даже читать), чем в случае простого запускаемого потока.источник
Одна из причин, по которой вы хотите реализовать интерфейс, а не расширять базовый класс, состоит в том, что вы уже расширяете какой-то другой класс. Вы можете расширить только один класс, но вы можете реализовать любое количество интерфейсов.
Если вы расширяете Thread, вы в основном препятствуете выполнению вашей логики любым другим потоком, кроме 'this'. Если вы хотите, чтобы какой-то поток выполнял вашу логику, лучше просто реализовать Runnable.
источник
если вы используете runnable, вы можете сэкономить место для расширения до любого другого вашего класса.
источник
Можем ли мы еще раз посетить основную причину, по которой мы хотели, чтобы наш класс вел себя как
Thread
? Нет никакой причины, мы просто хотели выполнить задачу, скорее всего, в асинхронном режиме, что точно означает, что выполнение задачи должно происходить из нашего основного потока и основного потока, если он заканчивается раньше, может ждать или не ждать для разветвленной дорожки (задача).Если в этом вся цель, то где я вижу необходимость в специализированной теме? Это можно сделать, выбрав поток RAW из пула потоков системы и назначив ему нашу задачу (может быть, экземпляром нашего класса), и это все.
Итак, давайте подчинимся концепции ООП и напишем класс нужного нам типа. Есть много способов сделать что-то, правильное решение имеет значение.
Нам нужна задача, поэтому напишите определение задачи, которое можно запустить в потоке. Так что используйте Runnable.
Всегда помните
implements
, специально используется для передачи поведения иextends
используется для передачи свойства / свойства.Нам не нужно свойство потока, вместо этого мы хотим, чтобы наш класс вел себя как задача, которую можно запустить.
источник
Да, если вы вызываете вызов ThreadA, то нет необходимости вызывать метод start, а метод run вызывается только после вызова класса ThreadA. Но если использовать вызов ThreadB, то необходимо вызвать начальный поток для вызова метода run. Если вам нужна помощь, ответьте мне.
источник
Я считаю, что наиболее полезно использовать Runnable по всем упомянутым причинам, но иногда мне нравится расширять Thread, чтобы я мог создать свой собственный метод остановки потока и вызывать его непосредственно в созданном мной потоке.
источник
Java не поддерживает множественное наследование, поэтому, если вы расширяете класс Thread, никакой другой класс расширяться не будет.
Например: если вы создаете апплет, то он должен расширять класс Applet, поэтому здесь единственный способ создать поток - реализовать интерфейс Runnable.
источник
Runnable
это интерфейс, в то времяThread
как это класс, который реализует этот интерфейс. С точки зрения дизайна, должно быть четкое разделение между тем, как задача определена, и тем, как она выполняется. Первый является обязанностьюRunnalbe
реализации, а второй - работаThread
класса. В большинстве случаев реализацияRunnable
является правильным способом следовать.источник
Разница между Thread и runnable. Если мы создаем Thread с использованием класса Thread, тогда номер потока равен числу созданных нами объектов. Если мы создаем поток с помощью реализуемого интерфейса, то мы можем использовать один объект для создания нескольких потоков. Таким образом, один объект совместно используется несколькими потоками. Так что это займет меньше памяти
Таким образом, в зависимости от требования, если наши данные не чувствительны. Таким образом, он может быть разделен между несколькими потоками, мы можем использовать интерфейс Runnable.
источник
Добавляя мои два цента здесь - Всегда, когда это возможно, используйте
implements Runnable
. Ниже приведены два предостережения о том, почему вы не должны использоватьextends Thread
SВ идеале вы никогда не должны расширять класс Thread;
Thread
класс должен быть сделанfinal
. По крайней мере, его методы, какthread.getId()
. Смотрите это обсуждение для ошибки, связанной с расширениемThread
s.Те, кто любит разгадывать головоломки, могут увидеть еще один побочный эффект расширения Thread. Приведенный ниже код будет печатать недоступный код, когда никто не уведомляет их.
Пожалуйста, смотрите http://pastebin.com/BjKNNs2G .
источник