26/08/2026

Многопоточность. Потоки

курс stepic

Process and Threads

Процесс (process)

  • Любое приложение, которое вы запускаете, есть некий процесс.
    У него собственный набор ресурсов и собственный участок памяти, к которым не могут обращаться другие процессы.

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

  • Многопроцессное приложение можно запускать на несколких компьютерах.

Потоки (threads)

  • Потоки не являются отдельным приложением. Потоки живут внутри какого то приложения, они живут в одном процессе.

  • У каждого процесса есть как минимум один поток - это главный поток исполнения (main). Кроме главного потока у процесса могут быть еще несколько других потоков.

  • В отличие от процессов, потоки делят между собой память и другие ресурсы приложения в котором они запущены. Если у вас процесс и у процесса память и в процессе два потока, они смогут через этот heap между собой передавать какие то данные. Это значит что есть объект в хипе на который есть ссылка из стеков двух потоков и в процессе работы из двух этих потоков по ссылке можно к этому объекту обратиться.

  • Потоки могут порождать другие потоки и взаимодействовать с ними.

Поток в коде

Это объект, у класса которого есть методы start() и run().

При вызове метода start() через какое то время, которое вы не которолируете, операционная система позовет у этого объекта метод run(). Если точнее, операционная система создаст условия, при которых начнется выполнение команд внутри метода run(). Это выполнение начнется в параллель метода main или каких то других методов run() других потоков.

Но создавая потоки, мы не получаем от операционной системы гарантии последовательности исполнения методов run(). Вы вызываете только методы start(), а в какой момент запустятся методы run() вы не которолируете.

Роль операционной системы

Можно в своем приложении создать некий набор команд для операционной системы попросив ее сделать какие то вещи связанные с потоками.

Сам процесс создания потока, переключение между потоками, какой именно из потоков будет исполнен на каком процессоре решает операционная система.

Также операционная система предоставляет некое API, в котором можно попросить создать и сделать некое уведомление. На основе чего можно запускать поток по получении какого либо события, как и пробуждение потока.

Создание потока

Создать класс, который будет являться объектом потока.

Есть 3 возможности:

  1. Создать класс который реализует интерфейс Runnable и определяет метод run(). При создании объекта класса Thread передать в него объект класса Runnable и вызвать метод start().
public class HelloRunnable implements Runnable {
    
    @Override
    public void run() {
        System.out.println("Hello from a thread!");
    }
    public static void main(String[] args)  {
        new Thread(new HelloRunnable()).start();
    }

}
  1. Наследоваться от класса Thread и от объекта самого этого класса вызвать start().
public class HelloThread extends Thread {

    @Override
    public void run() {
        System.out.println("Hello from a thread!");
    }
    public static void main(String[] args)  {
        new HelloThread().start();
    }

}
  1. Через лямбду
public class HelloThread {

    public static void main(String[] args) {
        new Thread(() ->
                System.out.println("Hello from a thread!")
        ).start();
    }

}

Runnable vs Thread

Runnable

  • можно наследовать что-нибудь ещё
  • нет методов управления потоком, поэтому его нужно передавать в объект Thread

Thread

  • можно наследовать только Thread и больше ничего
  • содержит методы управления потоком

И для Runnable и для Thread можно получить ссылку на объект текущего потока через статический метод Thread thread = Thread.currentThread().

Методы класса Thread

id

long getId()

у каждого потока есть id

priority

int getPriority()
void setPriority(int priority)

у каждого потока есть приоритет по которому операционная система решает как много ресурсов каждому потоку нужно выдавать.

sleep

static void sleep(long ms)

Если нужно остановить выполнение потока на определенное время

Thread.sleep(1000) - остановит выполнение потока на 1 секунду(он не проснется раньше, но через сколько точно он проснется решает операционная система)

interrupt

void interrupt()
static boolean interrupted()

Возможность разбудить поток. Сам поток проснется через определенное время. Но мы можем разбдить его например из другого потока. Это происходит через выброс исключения InterupptedException - это в случае если поток например спит, а если не спит то будет проставлен флаг Thread.interrupted() = true. И поток сам должен проверить не установил ли его кто-нибудт в true, чтобы прервать работу.

join

void join()

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