Ну, так много хороших ответов, я хочу добавить еще об этом. Это поможет понять, Extending v/s Implementing Thread
.
Extends очень тесно связывает два файла классов и может привести к некоторым трудностям при работе с кодом.
Оба подхода выполняют одну и ту же работу, но есть некоторые различия.
Наиболее распространенная разница
- Когда вы расширяете класс Thread, после этого вы не можете расширить любой другой класс, который вам требуется. (Как вы знаете, Java не позволяет наследовать более одного класса).
- Когда вы реализуете 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 the 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 и передать себя в качестве цели.
В большинстве случаев интерфейс Runnable следует использовать, если вы планируете переопределить только метод run()
, а не другие методы Thread. Это важно, потому что классы не должны быть разделены на подклассы, если программист не намеревается изменить или улучшить фундаментальное поведение класса.
Когда необходимо расширить суперкласс, реализация интерфейса Runnable более подходит, чем использование класса Thread. Потому что мы можем расширить другой класс при реализации интерфейса Runnable для создания потока.
Надеюсь, это поможет!