undefined

Создание и запуск новых нитей (трэдов)

Java Core
6 уровень , 2 лекция
Открыта

— Привет, Амиго! Элли рассказала тебе о нитях, а я расскажу тебе о том, как с ними работать. Чтобы породить новую нить нужно:

1) Создать объект класса Thread (нить)

2) Передать в него метод, который нужно выполнить

3) Вызвать у созданного объекта Thread метод start.

Пример:

Код Описание
class Printer implements Runnable
{
public void run()
{
System.out.println("I’m printer");
}
}
Класс, который реализует интерфейс Runnable.
public static void main(String[] args)
{
Printer printer = new Printer();
Thread childThread = new Thread(printer);
childThread.start();
}
1 Создали объект класса Printer, который содержит метод run.
2 Создали новый объект класса Thread, передали ему в конструкторе объект printer, чей метод run()нужно будет исполнить.
3 Запустили новую нить в работу, вызовом метода start().

Маленькие программы на Java обычно состоят из одной нити, называемой «главной нитью» (main thread). Но программы побольше часто запускают дополнительные нити, их еще называют «дочерними нитями». Главная нить выполняет метод main и завершается. Аналогом такого метода main, для дочерних нитей служит метод run интерфейса Runnable.

— Ага, много нитей, много методов main.

Создание и запуск новых нитей (трэдов) - 1

— Чтобы указать, с какого именно метода нужно начать выполнение объекту Thread, нужно как-то передать метод этому объекту. В Java это реализовано с помощью интерфейса Runnable. Этот интерфейс содержит единственный абстрактный метод – void run(). Класс Thread имеет конструктор Thread(Runnable runnable), в который можно передать любой объект, который реализует интерфейс Runnable.

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

Код Описание
class Printer implements Runnable
{
private String name;
public Printer(String name)
{
this.name = name;
}
public void run()
{
System.out.println("I’m " + this.name);
}
}
Класс, который реализует интерфейс Runnable.
public static void main(String[] args)
{
Printer printer1 = new Printer("Коля");
Thread thread1 = new Thread(printer1);
thread1.start();

Printer printer2 = new Printer("Вася");
Thread thread2 = new Thread(printer2);
thread2.start();
}
Создаем две нити, каждая на основе своего объекта типа Printer.
public static void main(String[] args)
{
Printer printer = new Printer("Наташа");

Thread thread1 = new Thread(printer);
thread1.start();

Thread thread2 = new Thread(printer);
thread2.start();

Thread thread3 = new Thread(printer);
thread3.start();
}
Создаем три нити, на основе одного объекта Printer.

Более того, можно совместить это все в одном классе. Класс Thread унаследован от интерфейса Runnable, и достаточно просто переопределить его метод run:

Второй способ создания новой нити
class Printer extends Thread
{
private String name;
public Printer(String name)
{
this.name = name;
}
public void run()
{
System.out.println("I’m " + this.name);
}
}
Унаследовались от класса Thread, который реализует интерфейс Runnable, и переопределили метод run.
public static void main(String[] args)
{
Printer printer = new Printer("Вася");
printer.start();

Printer printer2 = new Printer("Коля");
printer2.start();

}
Создаем две нити, каждая на основе своего объекта типа Printer.

— Это решение красивее.

— Да, но у него есть минусы:

1) Вам может понадобиться запустить несколько нитей на основе одного единственного объекта, как это сделано в «примере с Наташей».

2) Если вы унаследовались от класса Thread, вы не можете добавить еще один класс-родитель к своему классу.

3) Если у вашего класса есть класс-родитель, вы не можете добавить второго – Thread.

— Т.е. каждая из нитей после вызова метода start начнет выполнять метод run того объекта, который ему передали в конструкторе?

— Да. А если в конструкторе ничего не передали, то Thread просто исполняет свой внутренний метод run.

— А почему нельзя просто вызвать этот метод, например, так:

Код
public static void main(String[] args)
{
 Printer printer1 = new Printer("Коля");
 printer1.run();
}

— Когда главная нить дойдет до метода run, наш «маленький роботик», просто зайдет внутрь и начнет исполнять все команды, которые там есть внутри, и только после их выполнения, вернется в метод main, и продолжит работу дальше. Создания второго «маленького робота» не произойдет, и вся работа будет делаться последовательно, а не параллельно (одновременно).

— Ясно. А можно вызвать какой-нибудь другой метод, а не run?

— Нет. Все привязано к интерфейсу Runnable, а он «знает» только об одном своем методе – run.

Комментарии (201)
Чтобы просмотреть все комментарии или оставить свой,
перейдите в полную версию
Таймураз Вазиев 16 уровень, Казань
6 апреля 2021
Пожалуйста подскажите, а что мне мешает унаследоваться от Thread и на основе одного объекта создать несколько нитей и передать в них тот самый объект? 1)... Вроде как пример с Наташей работает при наслеловании от Thread, минуя implements Runnable.
Maks Panteleev 25 уровень, Москва
2 апреля 2021
Как мне выдержать то обстоятельство, что они упорно называют потоки нитями?
"Почему бы и да" 29 уровень
27 марта 2021
Топовый комментарий: Денис 40 уровень, Москва В качестве основного преимущества при наследовании класса Thread заявляется полный доступ ко всем функциональным возможностям потока на платформе Java. Главным недостатком же считается как раз сам факт наследования, так как в силу того, что в Java применяется только одиночное наследование, то наследование классу Thread автоматически закрывает возможность наследовать другим классам. Для классов, отвечающих за доменную область или бизнес-логику, это может стать серьезной проблемой. Правда негативное воздействие, возникающее из-за невозможности наследования, можно ослабить, если вместо него применить прием делегирования или соответствующие шаблоны проектирования. Использование интерфейса Runnable по умолчанию лишено этого недостатка, но если реализовать задачу таким способом, то придется потратить дополнительные усилия на ее запуск. Как было показано в листинге 2, для запуска Runnable-задачи все равно потребуется объект Thread, также в этом случае исчезнет возможность прямого управления потоком из задачи. Поэтому сделать однозначный вывод о превосходстве какого-либо подхода довольно сложно, и чаще всего в приложениях одновременно используются оба варианта, но для решения задач различной направленности. Считается, что наследование класса Thread следует применять только тогда, когда действительно необходимо создать «новый вид потока, который должен дополнить функциональность класса java.lang.Thread», и подобное решение применяется при разработке системного ПО, например, серверов приложений или инфраструктур. Использование интерфейса Runnable показано в случаях, когда просто «необходимо одновременно выполнить несколько задач» и не требуется вносить изменений в сам механизм многопоточности, поэтому чаще используется вариант с интерфейсом Runnable.
Арсений 32 уровень, Москва
25 марта 2021
ой как я орнул с многопоточной Наташи ))
Алексей 28 уровень, Оренбург
29 января 2021
А если при реализации через наследование Thread не переопределять метод run()? Так же поток будет работать?
🦔 Виктор 20 уровень, Москва Expert
13 января 2021
Ничего не понял, но вам меня не сломить, пошёл читать про многопоточность в Java на Хабре... Ведь я не для того, зашёл так далеко, чтобы слиться на каких-то нитках. Всё получится!
Александр Казеев 27 уровень, Киев
15 декабря 2020
Объект с именем Наташа запустили на три потока🤣 Какая-то турецкая программа получается😂
Ivan Valeryevich 22 уровень, Белгород
13 декабря 2020
Наконец то интересное пошло)) Скоро буду асинхронные проги мутить :D
Елена 17 уровень
12 декабря 2020
"Класс Thread унаследован от интерфейса Runnable". класс не наследует интерфейс, а реализует его
vladimir1 16 уровень, Харьков
8 декабря 2020
отличный сайт https://metanit.com/java/tutorial/8.4.php теорию оттуда изучать легче чем из сканированной книги -больше цветов, наглядности, да и лучше адаптировано к русскому читателю