«Реализует Runnable» против «расширяет поток» в Java

1947

С какого времени я провел с потоками в 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

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

  • 42
    Спасибо за этот вопрос, ответы прояснили многие заблуждения, которые у меня были. Я искал правильный способ сделать потоки Java до появления SO, и там было много дезинформации / устаревшей информации.
  • 3
    есть одна причина, по которой вы можете расширить Thread ( но я не рекомендую это делать ), вы можете превентивно обрабатывать interrupt() . Опять же, это идея, она может быть полезна в правильном случае, однако я не рекомендую ее.
Показать ещё 10 комментариев
Теги:
multithreading
runnable
java-threads
implements

42 ответа

1557
Лучший ответ

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

В практическом плане это означает, что вы можете реализовать Runnable и перейти от другого класса.

  • 130
    Точно, хорошо поставлено. Какое поведение мы пытаемся переписать в Thread, расширяя его? Я бы сказал, что большинство людей не пытаются переписать какое-либо поведение, а пытаются использовать поведение Thread.
  • 79
    В качестве побочного комментария, если вы создаете экземпляр Thread и не вызываете его метод start (), вы создаете утечку памяти в Java <5 (этого не происходит с Runnables): stackoverflow.com/questions/107823/…
Показать ещё 31 комментарий
480

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());
  • 34
    Это лучше, чем принятый ответ ИМХО. Одно: фрагмент кода, который у вас есть, не закрывает исполнителя, и я вижу миллионы вопросов, в которых люди ошибаются, создавая нового исполнителя каждый раз, когда они хотят создать задачу. es было бы лучше в качестве статического (или внедренного) поля, поэтому оно создается только один раз.
  • 7
    @artbristol, спасибо! Я не согласен с новым исполнителем (мы делаем то, что вы предлагаете в нашем коде). При написании исходного ответа я пытался написать минимальный код, аналогичный исходному фрагменту. Мы должны надеяться, что многие читатели этих ответов используют их как трамплин. Я не пытаюсь написать замену для Javadoc. Я эффективно пишу маркетинговые материалы для этого: если вам нравится этот метод, вы должны увидеть все другие замечательные вещи, которые мы можем предложить ...!
Показать ещё 2 комментария
233

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

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

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

Наследовать меньше, больше интерфейса.

  • 1
    Это всегда должен быть вопрос, если вы начнете делать параллельный запуск Object! Вам даже нужны функции Thread Object?
  • 2
    При наследовании от Thread почти всегда требуется переопределить поведение метода run() .
Показать ещё 2 комментария
193

Ну так много хороших ответов, я хочу добавить еще об этом. Это поможет понять 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 стал Thread, вам нужно создать экземпляр Thread и передать себя в качестве цели.

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

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

Надеюсь, это поможет!

  • 33
    Ваш код явно неверен. Я имею в виду, он делает то, что делает, но не то, что вы намеревались показать.
  • 32
    Для пояснения: для случая запуска вы использовали один и тот же экземпляр ImplementsRunnable для запуска нескольких потоков, тогда как для случая потока вы создаете разные экземпляры ExtendsThread, что, очевидно, приводит к поведению, которое вы показали. Вторая половина вашего основного метода должна быть: 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(); Это понятнее?
Показать ещё 13 комментариев
76

Одна вещь, которую я удивляю, еще не упоминалась, заключается в том, что реализация Runnable делает ваш класс более гибким.

Если вы расширяете поток, то действие, которое вы делаете, всегда будет в потоке. Однако, если вы реализуете Runnable, это не обязательно. Вы можете запустить его в потоке или передать его какой-либо службе-исполнителю или просто передать его как задачу в однопоточном приложении (возможно, для запуска позднее, но в пределах одного потока). Опции намного более открытые, если вы просто используете Runnable, чем если бы вы привязали себя к Thread.

  • 6
    Ну, на самом деле вы можете сделать то же самое с объектом Thread потому что Thread implements Runnable … ;-) Но это приятнее делать это с Runnable чем делать это с Thread !
  • 7
    Верно, но Thread добавляет множество дополнительных вещей, которые вам не нужны, а во многих случаях не нужны. Вам всегда лучше реализовывать интерфейс, который соответствует тому, что вы на самом деле делаете.
70

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

Самое распространенное различие -

Изображение 1315

Когда вы 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, он разделяет один и тот же объект на несколько потоков.

64

Собственно, нецелесообразно сравнивать Runnable и Thread друг с другом.

У этих двух есть зависимость и взаимосвязь в многопоточности, как и отношение Wheel and Engine транспортного средства.

Я бы сказал, есть только один способ многопоточности с двумя шагами. Позвольте мне высказать свою мысль.

Runnable:
При реализации interface Runnable это означает, что вы создаете нечто, что run able в другом потоке. Теперь создание чего-то, что может выполняться внутри потока (runnable внутри потока), не означает создание потока.
Таким образом, класс MyRunnable представляет собой не что иное, как обычный класс с методом void run. И это объекты будут обычными объектами только с методом run, который будет нормально выполняться при вызове. (если мы не передадим объект в потоке).

Тема:
class Thread, я бы сказал, очень специальный класс с возможностью запуска нового потока, который фактически позволяет многопоточность через метод start().

Почему не разумно сравнивать?
Потому что нам нужны оба для многопоточности.

Для многопоточности нам нужны две вещи:

  • Что-то, что может работать внутри Thread (Runnable).
  • Что-то, что может начать новый поток (Thread).

Таким образом, технически и теоретически, оба из них необходимы для запуска потока, один из них будет запускать, а один будет запустит его (как Wheel and Engineсильным автомобилем).

Чтобы вы не могли начать поток с MyRunnable, вам нужно передать его в экземпляр Thread.

Но можно создать и запустить поток только с помощью class Thread, потому что Class Thread реализует Runnable, поэтому мы все знаем, что Thread также является Runnable внутри.

Наконец Thread и Runnable дополняют друг друга для многопоточности, а не для конкурента или замены.

  • 3
    Именно так! Это должен быть принятый ответ. Кстати, я думаю, что вопрос был отредактирован и ThreadA больше не имеет смысла
  • 0
    принятый ответ намного больше делегат спасибо за ваш ответ @idelvall
43

Вы должны реализовать Runnable, но если вы работаете на Java 5 или выше, вы не должны запускать его с помощью new Thread, но используйте ExecutorService. Подробнее см. Как реализовать простую поточную передачу в Java.

  • 5
    Я не думаю, что ExecutorService будет настолько полезен, если вы просто захотите запустить один поток.
  • 1
    Из того, что я узнал, больше не нужно вообще запускать поток самостоятельно, потому что предоставление этого сервису-исполнителю делает все намного более управляемым (например, ожидание приостановки потока). Кроме того, я не вижу ничего в этом вопросе, что подразумевает, что речь идет об одной теме.
Показать ещё 2 комментария
30

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

Изменить: это изначально говорилось: "Для реализации интерфейса требуется меньше ресурсов". также, но вам нужно создать новый экземпляр Thread любым способом, так что это было неправильно.

  • 0
    В runnable мы не можем делать сетевые вызовы, не так ли? Поскольку у меня есть android.os.NetworkOnMainThreadException. Но с помощью потока я могу делать сетевые звонки. Пожалуйста, поправьте меня, если я ошибаюсь.
  • 0
    @NabeelThobani Обычная Java не заботится, но звучит, как Android. Я не достаточно знаком с Android, чтобы сказать, хотя.
Показать ещё 1 комментарий
19

Я бы сказал, что есть третий способ:

public class Something {

    public void justAnotherMethod() { ... }

}

new Thread(new Runnable() {
   public void run() {
    instanceOfSomething.justAnotherMethod();
   }
}).start();

Возможно, это немного повлияло на мое недавнее интенсивное использование Javascript и Actionscript 3, но таким образом вашему классу не нужно реализовывать довольно смутный интерфейс, например Runnable.

  • 38
    Это не совсем третий путь. Вы по-прежнему внедряете Runnable, просто делаете это анонимно.
  • 2
    @ Дон Роби: Который отличается. Это часто удобно, и вы можете использовать поля и конечные локальные переменные из содержащего класса / метода.
Показать ещё 2 комментария
16

С выпуском Java 8 теперь есть третий вариант.

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

Ваш пример может быть заменен на:

new Thread(() -> { /* Code here */ }).start()

или если вы хотите использовать ExecutorService и ссылку на метод:

executor.execute(runner::run)

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

  • 0
    Этот ответ требует объяснения. После некоторого недоумения я заключаю, что () -> {} должен представлять пользовательскую логику, которая кому-то нужна? Так что было бы лучше сказать как () -> { /* Code here */ } ?
  • 0
    @ToolmakerSteve да, отредактировано.
15

Создание интерфейса дает более четкое разделение между вашим кодом и реализацией потоков, поэтому я предпочел бы реализовать Runnable в этом случае.

12
  • Java не поддерживает множественное наследование, а это значит, что вы можете расширять только один класс на Java, поэтому, как только вы расширили класс Thread, вы потеряли свой шанс и не можете продлить или наследовать другой класс на Java.
  • В объектно-ориентированном программировании расширение класса обычно означает добавление новых функций, изменение или улучшение поведения. Если мы не вносим никаких изменений на Thread, а вместо этого используем интерфейс Runnable. Интерфейс
  • Runnable представляет собой Task, который может выполняться либо обычным Thread, либо Executors или любым другим способом. Поэтому логическое разделение Task как Runnable, чем Thread является хорошим конструктивным решением.
  • Разделительная задача как Runnable означает, что мы можем повторно использовать задачу, а также иметь возможность выполнять ее с разных средств. Поскольку вы не можете перезапустить Thread после завершения, снова Runnable vs Thread для задачи, Runnable является победителем.
  • Java-дизайнер распознает это и почему Executors принимает Runnable как Task, и у них есть рабочий поток, который выполняет эту задачу.
  • Наследование всех методов Thread - дополнительные накладные расходы только для представления Task, который можно легко выполнить с помощью Runnable.
10

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

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

  • В том же свете, если вам не нужны методы наследовать, вы можете обойтись без служебных с помощью Runnable.

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

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

  • Вы можете выполнить один и тот же Runnable объект несколько раз, но объект Thread может быть запущен только один раз. (Может быть, причина, почему исполнители действительно принимают Runnables, но не Threads.)

  • Если вы разрабатываете свою задачу как Runnable, у вас есть гибкость, как использовать ее сейчас и в будущем. Вы можете запустить его одновременно с помощью Executors, но также через Thread. И вы все равно можете использовать/вызывать его не одновременно в пределах одного потока, как и любой другой обычный тип/объект.

  • Это облегчает разделить задачу-логику и concurrency аспекты ваших модульных тестов.

  • Если вас интересует этот вопрос, вас также может заинтересовать разница между Callable и Runnable.

  • 0
    О пункте № 5: Thread реализует Runnable.
  • 0
    @Pino Да, сам поток также является Runnable. Однако, если вы расширяете его, чтобы просто использовать его как Runnable, какой смысл? Почему бы просто не использовать обычный Runnable без всего багажа. Итак, я бы сказал, что если вы расширяете Thread, вы также выполняете его, используя метод start, который можно использовать только один раз. Вот что хотел сделать Нидхиш-Кришнан в своем ответе. Обратите внимание, что мой просто компиляция или краткое изложение других ответов здесь.
10

Кажется, что все считают, что внедрение 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]
  • 5
    Thread.currentThread().setName("WhateverNameiFeelLike");
  • 0
    cHao в чем твоя точка зрения? Вы не можете использовать свой код выше во время выполнения потока, чтобы получить трассировку стека создания потоков (вместо этого вы получите простое имя или, в лучшем случае, трассировку стека запуска потоков), но с помощью подкласса потока вы можете сделать именно это и заставить это, даже требуя дополнительной информации, специфичной для контекста, тем самым давая вам более конкретное понимание того, в какой именно теме может возникнуть проблема.
Показать ещё 2 комментария
10

Runnable, потому что:

  • Предоставляет больше гибкости для Бегущая реализация для расширения другой класс
  • Отделяет код от выполнение
  • Позволяет запускать управляемый из пула потоков, поток событий или любым другим способом в будущее.

Даже если вам это не нужно сейчас, вы можете в будущем. Поскольку нет преимущества для переопределения Thread, Runnable - лучшее решение.

8

Это обсуждается в учебном пособии Oracle Определение и запуск потока:

Какую из этих идиом вы должны использовать? Первая идиома, в которой используется Runnable object, является более общим, поскольку объект Runnable может подкласс - класс, отличный от Thread. Вторая идиома проще в использовании в простых приложениях, но ограничивается тем, что ваша задача класс должен быть потомком Thread. Этот урок фокусируется на первом подход, который отделяет задачу Runnable от объекта Thread который выполняет задачу. Этот подход не только более гибкий, но он применим к API-интерфейсам управления потоками высокого уровня позже.

Другими словами, реализация Runnable будет работать в сценариях, где ваш класс расширяет класс, отличный от Thread. Java не поддерживает множественное наследование. Кроме того, расширение Thread будет невозможно при использовании некоторых высокоуровневых API управления потоками. Единственный сценарий, в котором расширение Thread предпочтительнее, - это небольшое приложение, которое в будущем не будет обновляться. Практически лучше реализовать Runnable, поскольку он более гибкий по мере роста вашего проекта. Изменение дизайна не окажет большого влияния, поскольку вы можете реализовать множество интерфейсов в java, но только расширяете один класс.

7

Разница между расширением потока и реализацией Runnable:

Изображение 1316

6

Если я не ошибаюсь, он более или менее похож на

В чем разница между интерфейсом и абстрактным классом?

extends устанавливает " Is A" отношение и интерфейс обеспечивает " имеет".

Предпочитает реализует Runnable:

  • Если вам не нужно расширять класс Thread и изменять реализацию по умолчанию API нитей
  • Если вы выполняете пожар и забываете команду
  • Если вы уже расширяете другой класс

Предпочитаете " расширять поток":

  • Если вам нужно переопределить любой из этих Thread методов, перечисленных на странице документации oracle

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

С другой стороны, использование расширенного API ExecutorService или ThreadPoolExecutorService обеспечивает большую гибкость и контроль.

Взгляните на этот вопрос СЕ:

ExecutorService vs Casual Thread Spawner

5

Можем ли мы повторно посетить основную причину, по которой мы хотели, чтобы наш класс вел себя как Thread? Нет никакой причины, мы просто хотели выполнить задачу, скорее всего, в асинхронном режиме, что точно означает, что выполнение задачи должно входить из нашего основного потока и основного потока, если заканчивается раньше, может или не может ждать для разветвленного пути (задачи).

Если это целая цель, то где я вижу потребность в специализированном потоке. Это может быть достигнуто путем сбора RAW-потока из пула системных потоков и назначения его нашей задачи (может быть экземпляром нашего класса), и это все.

Итак, давайте повинуемся концепции ООП и напишем класс типа, который нам нужен. Есть много способов сделать что-то, делайте это правильно.

Нам нужна задача, поэтому напишите определение задачи, которое можно запустить в потоке. Поэтому используйте Runnable.

Всегда помнить implements специально используется для придания поведения, а extends используется для передачи функции/свойства.

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

5

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

5

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

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

  • 0
    Да, благодаря реализации интерфейса Runnable, чтобы свободно реализовывать свою собственную логику, расширяя любой класс, поэтому Runnable в основном предпочтительнее, чем класс Thread.
5

Отделить класс Thread от реализации Runnable также позволяет избежать возможных проблем синхронизации между потоком и методом run(). Отдельный Runnable обычно обеспечивает большую гибкость в том, как ссылающийся и исполняемый код ссылается и выполняется.

4

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

Я только что опубликовал пример, поэтому я также поделюсь этим с вами:

/**
 * 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();
    }

}
4

Простейшим объяснением будет реализация Runnable, которую мы можем назначить одному и тому же объекту нескольким потокам, и каждый Thread имеет общие состояния и поведение объектов.

Например, предположим, что существует два потока, thread1 помещает целое число в массив, а thread2 принимает целые числа из массива, когда массив заполняется. Обратите внимание: для того, чтобы thread2 работал, он должен знать состояние массива, независимо от того, заполнил ли он thread1 или нет.

Реализация Runnable позволяет использовать эту гибкость для совместного использования объекта, тогда как extends Thread позволяет создавать новые объекты для каждого потока, поэтому любое обновление, которое выполняется thread1, теряется для thread2.

4

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

Класс, реализующий Runnable, не является потоком и просто классом. Чтобы Runnable выполнялся потоком, вам нужно создать экземпляр Thread и передать экземпляр Runnable в качестве цели.

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

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

4

Добавление моих двух центов здесь - Всегда, когда это возможно, используйте 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 ");
        }
    } 
}
4

Разница между потоком и runnable . Если мы создаем Thread с использованием класса Thread, тогда количество потоков равно количеству созданного нами объекта. Если мы создаем поток, реализуя интерфейс runnable, мы можем использовать один объект для создания нескольких потоков. Один объект разделяется несколькими Thread.So он будет потреблять меньше памяти

Таким образом, в зависимости от требования, если наши данные не являются senstive. Таким образом, он может быть разделен между несколькими Thread, которые мы можем использовать интерфейс Runnable.

4

Что S SOLID Единая ответственность.

Нить воплощает выполняемый контекст (как в контексте выполнения: стек стека, идентификатор потока и т.д.) асинхронного выполнения части кода. Эта часть кода идеально должна быть одной и той же реализацией, будь то синхронной или асинхронной.

Если вы объединяете их вместе в одну реализацию, вы даете результирующему объекту две несвязанные причины изменения:

  • обработка потоков в вашем приложении (т.е. запрос и изменение контекста выполнения)
  • реализованный частью кода (исполняемой частью)

Если язык, который вы используете, поддерживает частичные классы или множественное наследование, вы можете разделить каждую причину в своем собственном суперклассе, но это сводится к тому же, что и для создания двух объектов, поскольку их набор функций не перекрывается. Это для теории.

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

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

4

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

Например: если вы создаете апплет, то он должен расширять класс Applet, поэтому здесь единственный способ создать поток - реализовать интерфейс Runnable

4

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

4

Да, Если вы вызываете вызов ThreadA, тогда не нужно вызывать метод start, а метод run - это вызов после вызова класса ThreadA. Но если использовать ThreadB-вызов, тогда необходимо создать начальный поток для метода вызова. Если у вас есть дополнительная помощь, ответьте мне.

3

Поведение содержит теги, которые не предназначены для доступа;

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

однако, если вы, суб-класс 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
3

Runnable - это интерфейс, а Thread - класс, реализующий этот интерфейс. С точки зрения дизайна, должно быть чистое разделение между тем, как определяется задача и между тем, как она выполняется. Первый отвечает за реализацию Runnalbe, а последняя - в классе Thread. В большинстве случаев реализация Runnable - правильный путь.

2

Расширяя класс потока, производный класс не может расширять любой другой базовый класс, потому что java допускает только одно наследование. напротив, путем реализации интерфейса runnable класс по-прежнему распространяет другой базовый класс.

Ниже приведено наиболее существенное различие между реализацией Runnable и продолжением Thread:

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

2

В редком случае вы запускаете его только один раз, вы должны продлить Thread из-за DRY. Если вы вызываете его несколько раз, вы должны реализовать Runnable, потому что тот же поток не следует перезапускать.

2

Это может быть не ответ, но в любом случае; есть еще один способ создания потоков:

Thread t = new Thread() {
    public void run() {
        // Code here
    }
}
1

Класс Thread определяет несколько методов, которые могут быть overriden расширенным классом. Но для создания потока мы должны переопределить метод run(). То же самое относится и к Runnable.

Однако Runnable является предпочтительным способом создания потока. Основные причины:

  • Так как Runnable - это интерфейс, вы можете расширить другие классы. Но если вы продляете Thread, то этот вариант не будет.

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

  • 0
    Само по себе дополнение к Runnable - это не поток, а просто задача, которую поток может запустить.
1

Простой способ сказать: Если вы реализуете интерфейс, который означает, что вы реализуете все его методы, и если вы расширяете класс, вы наследуете метод по вашему выбору... В этом случае существует только один метод с именем Run(), поэтому лучше реализовать интерфейс Runnable.

0

Основное различие между Thread и Runnable: - Thread is like: Worker (выполнить Runnable) - Runnable - это как: Job (для выполнения Thread)

0

Я бы сказал, что фактическая задача отделена от потока. Мы можем передать задачу в Thread, Executor framework и т.д. В случае Runnable, тогда как с расширением задачи Thread связано с самим объектом потока. Изоляция задачи не может быть выполнена в случае расширения Thread. Это похоже на то, что мы сжигаем задачу для объекта Thread только что-то вроде IC-чипа (и, более конкретно, не будем обрабатывать задачу).

-3

Вы можете использовать их совместно

Пример:

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

    }

} 
  • 0
    Проголосуйте, пожалуйста, объясните, почему.
  • 0
    Я не отрицал вас, но вы должны создать поток через new Thread(new A()) и вызвать метод start() вместо run() .
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню