Какая разница между разными способами запуска потока?
У меня есть класс с именем MyThread
, который расширяет класс Thread и реализует функцию run()
. Когда я хочу запустить его, у меня есть два способа:
- введите новый экземпляр и вызовите функцию, например:
new MyThread().start()
- введите новый экземпляр и передайте экземпляр функции построения Thread в качестве параметра, а затем вызовите функцию начала Thread. Например:
(new Thread(new MyThread)).start();
Кто-нибудь может просто сказать разницу?
Ответы
Ответ 1
Ну, у вас есть два способа реализовать многопоточность.
-
растяните Thread
и используйте new MyThreand().start()
, чтобы начать свой поток.
-
реализовать Runnable
интерфейс. В этом состоянии вы можете использовать (new Thread(new MyThread)).start()
; для запуска потока.
Для получения подробной информации просто обратитесь к официальному документу oracle.
Ответ 2
Поскольку вы сказали, что ваш класс extends Thread
, второй из них немного избыточен. Во втором примере вы не используете свой класс как Thread
, вы просто используете его как Runnable
.
Как правило, вы либо расширяете Thread
, а затем вызываете свой собственный start
(ваш # 1), или, который вы реализуете Runnable
, а затем используйте Thread
для запустите его (ваш # 2). Но вы не должны расширять Thread
, а затем использовать другой Thread
для его запуска.
С точки зрения того, что по-другому, если вам нужно что-либо сделать для контроля или опроса потока, в первом случае вы должны использовать методы Thread
в экземпляре вашего класса; во втором случае вы будете использовать их в экземпляре, создаваемом с помощью new Thread
. Если вы продляете Thread
, но запустите его через # 2, методы Thread
в вашем экземпляре не имеют значения и могут запутать.
Этот последний бит, вероятно, более ясен с примерами:
Пример расширения Thread
:
class Foo extends Thread {
public void run() {
// ...
}
}
// Create it
Foo foo = new Foo();
// Start it
foo.start();
// Wait for it to finish (for example)
foo.join();
Обратите внимание, что мы начали и присоединили поток через ссылку foo
.
Пример реализации Runnable
:
class Foo implements Runnable {
public void run() {
// ...
}
}
// Create it
Foo foo = new Foo();
// Create a Thread to run it
Thread thread = new Thread(foo);
// Start it
thread.start();
// Wait for it to finish (for example)
thread.join();
Обратите внимание, что мы начали и присоединили поток через ссылку Thread
.
Не делайте этого:
class Foo extends Thread {
public void run() {
// ...
}
}
// Create it
Foo foo = new Foo();
// Create a Thread to run it -- DON'T do this
Thread thread = new Thread(foo);
// Start it
thread.start();
... потому что теперь у вас есть Thread#join
, доступный как на foo
, так и на Thread
; который является правильным использовать? (Ответ таков: один на Thread
, но он запутан, поэтому лучше не делать этого.)
Ответ 3
Экземпляр MyThread
, который вы передаете, действует как Runnable
, а не как отдельный поток.
В принципе логически нет различий в обоих направлениях:
Что Thread
внутренне делает на start()
, вызывает метод run()
Runnable
.
И когда вы делаете new Thread(new MyThread()).start()
, вы просто избыточны, поскольку Thread
сам реализует Runnable
.
Но это не будет иметь никакого отношения логически, поскольку метод run()
MyThread
будет вызываться новым потоком.
Ответ 4
Вы не должны этого делать. Создание потока изменяет некоторые переменные, такие как "число нестационарных потоков" в ThreadGroup
.
В то время как это не должно точно вызывать проблемы, это плохой стиль и путает людей ( "Почему он это сделал? Должна быть веская причина!" ).
Ответ 5
Если ваш класс сам расширяет Thread
, вы можете следовать своему первому пути:
MyThread myThread = new MyThread();
myThread.start();
Если вы посмотрите на JavaDoc, вы увидите, что ваш второй способ ориентирован на классы, которые (просто) реализуют Runnable
( это означает, что вашему классу просто нужно реализовать метод run()
).
public class MyClass implements Runnable {
public void run() { ... }
}
Thread thread = new Thread(new MyClass());
thread.start();
Различие заключается в том, что Runnable
является только интерфейсом, тогда как Thread
является классом. Это означает, что если вы хотите, чтобы ваша логика была частью класса, который по какой-то причине должен расширять другой класс, вы все равно можете реализовать Runnable
и использовать второй способ.
Ответ 6
Тема - это реализация Runnable
. Создав экземпляр Thread
и запустив его, он выполнит собственный метод run()
. Если этому конструктору присвоен непустой Runnable target
, он будет вызывать метод target run()
, как это видно из реализованного метода run()
класса Thread
:
@Override
public void run() {
if (target != null) {
target.run();
}
}
Два способа создания нового потока выполнения:
-
Сначала нужно объявить класс (т.е. PThread
) подклассом Thread
: PThread extends PThread
и должен переопределить метод run
class
Thread. Затем мы можем легко создать экземпляр этого класса и вызвать на нем start()
:
PThread pThread = new PThread(args);
pThread.start();
-
Во-вторых, объявить класс, реализующий интерфейс Runnable
: RThread implements Runnable
, затем реализует метод run
. Нам нужно создать экземпляр RThread
и передать его в экземпляр Thread
как цель Runnable
:
RunThread rThread = new RunThread(args); // args constructor argument if required
Thread t = new Thread(rThread);
t.start();
Как вы видели из вышеприведенного способа запуска потока, на самом деле разрешен класс, расширяющий Thread
, например PThread
, который должен быть передан новому конструктору Thread
в качестве цели Runnable
для Начни это. Потому что Thread
сам является реализацией Runnable
.
PThread pThread = new PThread(args);
Thread t = new Thread(pThread);
// allowed as pThread is extending Thread and hence,
//an implementation of Runnable
t.start();
Но эта операция не нужна или, скорее, вы не должны этого делать. Поскольку PThread
уже есть Thread
, мы могли бы просто вызвать pThread.start()
для выполнения его метода run()
, который является сердцем Thread
. Создание другого потока только для выполнения PThread
- это просто дополнительные накладные расходы, поскольку он не будет выполнять ничего, кроме выполнения метода PThread
run()
.
Однако вы не должны использовать расширение Thread
вообще. Потому что
реализация Runnable всегда предпочтительнее расширения Thread:
-
Наследование всех методов Thread - это дополнительные накладные расходы только для представления задачи, которая может быть легко выполнена с помощью Runnable
.
-
Внедрение Runnable
в класс по-прежнему позволяет нам распространять его на другой класс, если это необходимо.
-
В OOP расширение класса обычно означает добавление новых функций, изменение или улучшение поведения. Если мы не будем вносить какие-либо изменения в Thread
или изменять его поведение, чем использовать Runnable interface
.
Интерфейс -
Runnable
представляет собой задачу, которая может выполняться либо обычным Thread
, либо Executors
или любым другим способом. поэтому логическое разделение Задачи как Runnable
, чем Thread
является хорошим дизайнерским решением.
-
Executors
, что облегчает жизнь при многопоточности, принимает Runnable
как задачу.
Справка:
Ответ 7
(новая тема (новый MyThread)). start();
каждая Thread реализует Runnable; в этом случае экземпляр MyThread используется как Runnable, новый поток вызывает метод выполнения, реализованный MyThread. Вы не сможете остановить поток (или управлять им любыми способами) с помощью MyThread.
Ответ 8
Нет абсолютно никакой разницы между тем, как вы начинаете Thread (class implementing Runnable or extending Thread class)
Это только абстракция, которую вы применяете, обратите внимание на (Runnable object, is more general, because the Runnable object can subclass a class other than Thread)
Хорошая практика кодирования.
In both of the cases Thread.start() will called