Тутор: многопоточность в Java
Современные процессоры содержат много вычислительных ядер (в т.ч. в телефонах) и поэтому вычисления многих задач можно ускорить подразбивая их на несколько подзадач и исполняя эти части на разных ядрах процессора параллельно.
В Java все формулируется ввиде объектов - экземпляров классов. Поэтому для многопоточности в Java есть класс Thread
, инстанцировав который возможно создать отдельный поток исполнения.
Чтобы создать Thread в Java надо написать такой код:
Thread t = new Thread(runnable);
Где аргумент runnable (переданный в конструктор) - Runnable
объект. Это можно увидеть открыв документацию по конструктору Thread (достаточно напечатать new Thread
, затем нажать Ctrl+Space
и Ctrl+Q
).
Чтобы понять, что такое Runnable
, стоит посмотреть его объявление. Это делается через горячую кнопку “переход к классу/интерфейсу” - Ctrl+N
и затем надо напечатать Runnable
и нажать Enter.
Как видно, кроме документации в Runnable определен лишь один метод:
public abstract void run();
Ключевое слово abstract говорит о том, что это является лишь декларацией/заявлением функции, но не ее определение. Иначе говоря, в этом месте Runnable заявляет (т.е. декларирует контракт) что у него обязательно есть функция с такой сигнатурой, но т.к. ее реализация может быть разной от случая к случаю - он сам ее не определяет. Не определяет он ее потому что Runnable - лишь интерфейс, т.е. обязательство на наличие такой функции.
Иначе говоря - если кто-то говорит, что он готов работать с Runnable (например функция принимает аргументом Runnable переменную, или как в случае с Thread - конструктор требует аргументом Runnable), то это
означает, что подходит любой класс, который реализует контракт Runnable, т.е. обязуется соблюсти все, что декларировано в этом интерфейсе. Что в нашем случае означает реализацию метода public void run()
.
Соответственно, чтобы создать такой класс надо написать примерно следующее:
public class MyRunnable implements Runnable {
public String name;
public int n;
public MyRunnable(String name, int n) {
this.name = name;
this.n = n;
}
public void run() {
for (int i = 0; i < n; i++) {
System.out.println(this.name + " - running " + i + "...");
}
System.out.println(this.name + " - finished!");
}
}
И тогда создать поток можно от экземпляра нашего класса:
MyRunnable runnable = new MyRunnable("MyRunnable", 10);
Thread t = new Thread(runnable);
Но поток не начинает работу в момент своего создания. Чтобы поток действительно начал свое исполнение - надо его запустить - t.start()
. Как можно прочитать в документации (курсор на start и Ctrl+Q
) - поток начнет исполнение метода run()
в отдельном потоке исполнения, т.е. параллельно.
Итого пример кода:
System.out.println("Starting spawning threads...");
for (int i = 0; i < 5; i++) {
MyRunnable runnable = new MyRunnable("MyRunnable#" + i, 10);
Thread t = new Thread(runnable);
t.start();
}
System.out.println("All threads started!");