"реализует Runnable" против "расширяет поток" в Java - PullRequest
1941 голосов
/ 12 февраля 2009

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

5 голосов
/ 25 июля 2013

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

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

5 голосов
/ 08 марта 2010

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

5 голосов
/ 22 ноября 2013

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

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

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

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

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

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

4 голосов
/ 09 июня 2012

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

4 голосов
/ 18 марта 2013

Это S из SOLID : единоличная ответственность.

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

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

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

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

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

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

4 голосов
/ 28 января 2012

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

4 голосов
/ 05 марта 2014

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

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

4 голосов
/ 19 февраля 2015

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

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

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

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

4 голосов
/ 09 июня 2012

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

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

4 голосов
/ 03 января 2015

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

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

  2. Те, кто любит разгадывать головоломки, могут увидеть еще один побочный эффект расширения 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 ");
        }
    } 
}
...