"реализует Runnable" vs "расширяет поток" в Java

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

С implements Runnable:

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

или extends Thread:

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

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

30 ответов


да: реализует Runnable является предпочтительным способом сделать это, ИМО. На самом деле вы не специализируетесь на поведении нити. Ты просто даешь ему возможность управлять. Это значит состав - это философски "чище" путь.

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


tl; dr: инструменты Runnable лучше. Однако нюанс важен

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

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

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

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

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

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

new FutureTask<Object>(threadA, null)

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

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

ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());

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

наследовать только, если вы хотите изменить какое-то поведение.

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

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


Ну так много хороших ответов, я хочу добавить больше об этом. Это поможет понять Extending v/s Implementing Thread.
Extends связывает два файла класса очень тесно и может вызвать некоторые довольно трудно справиться с кодом.

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

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

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

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

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

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

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

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

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


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

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


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

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

enter image description here

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

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

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

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

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

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

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

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

любезность javarevisited.blogspot.com

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

однако, существенная разница.

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


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

эти два имеют зависимость и отношения в многопоточности так же, как Wheel and Engine отношение автомобиля.

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

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

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

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

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

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

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

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

но создать и запустить поток можно только с помощью class Thread потому что класс Thread осуществляет Runnable Так мы все знаем!--1--> также является Runnable внутри.

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


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


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

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


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

public class Something {

    public void justAnotherMethod() { ... }

}

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

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


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

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

ваш пример можно заменить на:

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

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

executor.execute(runner::run)

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


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


  1. Java не поддерживает множественное наследование, что означает, что вы можете расширить только один класс в Java, поэтому, как только вы расширили Thread class вы потеряли свой шанс и не можете расширить или наследовать другой класс в Java.
  2. в объектно-ориентированном программировании расширение класса обычно означает добавление новых функций, изменение или улучшение поведения. Если мы не делаем никаких изменений на Thread чем использовать Runnable интерфейс вместо этого.
  3. Runnable интерфейс представлять a Task, который может быть выполнен либо простой Thread или Executors или любым другим способом. Так логично разделение Task as Runnable чем Thread хорошее проектное решение.
  4. разделение задач как Runnable означает, что мы можем повторно использовать задачу, а также иметь свободу выполнять ее из разных средств. Так как вы не можете перезапустить Thread Как только он завершается, снова Runnable vs Thread для задач, Runnable - это победитель.
  5. Java designer распознает это, и именно поэтому Executors принимать Runnable as Task и у них есть рабочий поток, который выполняет этих задач.
  6. наследуя все Thread методы являются дополнительными накладными расходами только для представления Task что можно сделать легко с Runnable.

Runnable потому что:

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

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


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

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

new Thread(myRunnable,"WhateverNameiFeelLike");

но если вы расширяете поток, то вы можете управлять этим внутри самого класса (как и в вашем примере, вы называете поток "ThreadB"). В этом случае вы:

A) может дать ему более полезное имя для целей отладки

B) заставляют, чтобы это имя использовалось для всех экземпляров этого класса (если вы не игнорируете тот факт, что это поток и делаете выше с ним, как будто это Runnable, но мы говорим о Конвенции здесь в любом случае, поэтому можете игнорировать эту возможность, которую я чувствую).

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

Это может показаться мелочью, но там, где у вас есть очень сложное приложение с большим количеством потоков, и внезапно все "остановилось" (либо по причинам тупика, либо, возможно, из - за недостатка в сетевом протоколе, который был бы менее очевидным-или по другим бесконечным причинам), то получение дамп стека из Java, где все потоки называются "Thread-1", "Thread-2", "Thread-3", не всегда очень полезен (это зависит от того, как структурированы ваши потоки и можете ли вы с пользой сказать, что есть что только по их трассировке стека - не всегда возможно, если вы используете группы из нескольких потоков, работающих под одним и тем же кодом).

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

вот пример общего потока с трассировкой вызывающего стека в качестве его имени:

public class DebuggableThread extends Thread {
    private static String getStackTrace(String name) {
        Throwable t= new Throwable("DebuggableThread-"+name);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(os);
        t.printStackTrace(ps);
        return os.toString();
    }

    public DebuggableThread(String name) {
        super(getStackTrace(name));
    }

    public static void main(String[] args) throws Exception {
        System.out.println(new Thread());
        System.out.println(new DebuggableThread("MainTest"));
    }
}

и вот пример вывода, сравнивающего два имени:

Thread[Thread-1,5,main]
Thread[java.lang.Throwable: DebuggableThread-MainTest
    at DebuggableThread.getStackTrace(DebuggableThread.java:6)
    at DebuggableThread.<init>(DebuggableThread.java:14)
    at DebuggableThread.main(DebuggableThread.java:19)
,5,main]

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

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

  2. в том же свете, если вам не нужен to наследование методы потоке, вы можете обойтись без этого накладных С помощью Runnable.

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

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

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

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

  7. Это также облегчает отдельные задача-логика и параллелизм аспекты в код тесты.

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


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

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

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


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

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

расширяет устанавливает "Это" & интерфейс связи "есть" возможности.

предпочитаю реализует интерфейс Runnable :

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

предпочитаю "расширяет нити" :

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

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

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

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

ExecutorService vs Casual Thread Spawner


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

enter image description here


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


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

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


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


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

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

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

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

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

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


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


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


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

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


Это S of твердый: один ответственности.

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

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

  1. обработка потоков в вашем приложении (т. е. запрос и изменение контекста выполнения)
  2. алгоритм, реализованный куском кода (runnable часть)

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

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

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


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

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


добавление моих двух центов здесь - всегда, когда это возможно, используйте implements Runnable . Ниже два предостережения о том, почему вы не должны использовать extends Threads

  1. в идеале вы никогда не должны расширять класс Thread;Thread класс должен быть final. По крайней мере, его методы, такие как thread.getId(). См.этой обсуждение ошибки, связанной с расширением Threads.

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

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

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

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

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