Процесс и поток
Чему вы научитесь:
- отличать процесс от потока и называть, что потоки одной программы делят между собой;
- запускать поток через
start(), дожидаться его черезjoin()и не путатьstart()сrun(); - объяснять, почему
count++из двух потоков теряет увеличения; - защищать общий счётчик с помощью
synchronizedиAtomicInteger.
Представьте кухню ресторана. На ней работают несколько поваров: один варит суп, другой режет салат, третий жарит котлеты. Работа идёт одновременно, и никто не ждёт, пока другой закончит своё блюдо.
У поваров есть общее и своё. Общее — холодильник с продуктами и доска на стене, где отмечают, сколько блюд отдано. Своё — блокнот, в котором повар считает, и место в рецепте, до которого он дошёл. А соседний ресторан стоит в отдельном здании со своей кухней, и в его холодильник наши повара не заглядывают.
Зачем это программисту? Почти любая серьёзная программа делает несколько дел сразу. Сервер отвечает тысячам пользователей, игра рисует кадр и в то же время загружает уровень. В Java для этого есть потоки, и в этой теме кухня — программа, а повара — её потоки.
Что значит «одновременно»
Команды программы выполняет процессор. В нём несколько ядер, и каждое ядро выполняет одну последовательность команд за раз:
- на четырёх ядрах четыре потока работают буквально одновременно;
- потоков обычно намного больше, чем ядер, поэтому операционная система переключает ядро с одного потока на другой много раз в секунду;
- со стороны кажется, что работают все сразу.
Кто и когда получит ядро, решает операционная система. Поэтому порядок, в котором работают потоки, не задан и меняется от запуска к запуску.
Процесс
Процесс — это запущенная программа со своей памятью. Браузер, музыкальный плеер и ваша программа на Java — три разных процесса. Операционная система отделяет их память друг от друга: один процесс не может прочитать переменные другого.
Команда java Main.java запускает один процесс — JVM, которая выполняет вашу программу. В нашей картинке процесс — это вся кухня с холодильником и доской, а соседний ресторан — другой процесс.
Поток
Поток — это последовательность команд, которая выполняется внутри процесса. Это повар на кухне: он идёт по своему рецепту строка за строкой. В одном процессе может работать много потоков, и все они пользуются памятью этого процесса.
Что у потоков общее
- Объекты в куче — всё, что создано через
new. Если у двух потоков есть ссылка на один объект, они работают с одним и тем же объектом. static-поля. Они принадлежат классу, а класс в программе один.
Это холодильник и доска на стене: их видят все повара.
Что у каждого потока своё
- Локальные переменные и параметры методов. Если два потока выполняют один и тот же метод, каждый получает свою копию его переменных.
- Место в программе — строка, которую поток выполняет сейчас.
Это блокнот повара и его место в рецепте.
Чем процесс отличается от потока
Этот вопрос часто задают на собеседовании. Ответ складывается из трёх пунктов:
- процесс — отдельная программа со своей памятью, а поток выполняется внутри процесса;
- потоки одного процесса делят кучу и
static-поля, а локальные переменные у каждого потока свои; - поток дешевле процесса: ему не нужно своё отдельное пространство памяти, поэтому его быстрее создать и на него быстрее переключиться.
Здесь аналогия с кухней неточна. Повара видят друг друга и могут договориться, кто подойдёт к доске первым. Потоки друг друга не видят: два потока могут взяться за один объект в одно и то же мгновение, и Java их не остановит.
Главный поток
Даже в программе, где вы не создавали ни одного потока, один поток есть. JVM выполняет main в потоке, который так и называется — main. Проверим:
public class Main {
public static void main(String[] args) {
String name = Thread.currentThread().getName();
System.out.println(name); // main
}
}Разберём по шагам:
Thread— класс из стандартной библиотеки: каждый поток в Java представлен объектом этого класса.Thread.currentThread()—static-метод, который возвращает объект того потока, что выполняет эту строку.getName()возвращает имя потока, и программа печатаетmain.
Вы уже встречали это имя в строке Exception in thread "main": так JVM сообщает, что исключение случилось в главном потоке. Кроме main, JVM запускает несколько служебных потоков для своей работы, так что даже самая короткая программа на Java многопоточная.
Частая ошибка
Имя потока спросили у самого класса Thread:
String name = Thread.getName(); // ✗error: non-static method getName() cannot be referenced from a static contextПрограмма не компилируется. getName() — метод объекта, а не класса: имя у каждого потока своё, и компилятору нужно знать, чьё именно. Сначала получите объект потока через Thread.currentThread(), а потом спросите его имя: Thread.currentThread().getName().
Главное
- Процесс — запущенная программа со своей памятью, а поток — последовательность команд внутри процесса.
- Потоки одного процесса делят объекты в куче и
static-поля, а локальные переменные у каждого потока свои. - Порядок, в котором работают потоки, решает операционная система, и от запуска к запуску он разный.
mainвыполняется в главном потоке с именемmain, аThread.currentThread()возвращает поток, который выполняет текущую строку.