Многопоточность [1]
Процессы и потоки
Когда мы запускаем программу, стартует процесс который "отражает" выполнение программы, то есть, содержит всю необходимую информацию для работы данной программы.
Каждый процесс имеет свое адресное пространство и последовательность исполнительных команд процессора.
Отделение этих частей (процессов) оправдано тем, что в рамках одного процесса может быть несколько исполнительных последовательностей команд, которые совместно используют одни и те же данные.
Набор таких команд и называется потоком. Он использует общее адресное пространство и исполняется процессором. Поскольку в системе одновременно может быть много потоков, задачей ОС является организация переключения процессора между ними и планирование их исполнения.
- защищенное адресное пространство
- данные, общие для всего процесса (эти данные могут использовать все его потоки)
- информация о использовании ресурсов (открытые файлы, сетевые подключение, и т. д.)
- информации о потоках процесса
Поток содержит такие элементы:
- состояние процессора (набор данных с его регистров), в частности счетчик текущей инструкции процессора (указатель на текущую инструкцию которая должна быть выполнена)
- стек потока (область памяти, где расположены локальные переменные потока и адреса возврата функций, вызванные в его коде)
Теперь можем дать определение многопоточности.
Многопоточность - это одновременное (с точки зрения программиста) исполнение каких-то действий различными фрагментами кода
Важное замечание: процессор в конкретный момент времени может исполнять инструкции только одного потока, по этому, существует планировщик, который распределяет процессорное время между потоками.
На основе алгоритмов такого планирования, определяют который из потоков нужно исполнить в конкретный момент, когда нужно нужно прервать исполнения потока, чтобы переключиться на другой поток.
Возможно возникнет вопрос.
Если в конкретный момент времени процессор может выполнять инструкции одного потока, тогда как же одновременно могут выполняться те или иные операции?
Если нагрузить одно ядро несколькими последовательностями задач, то оно будет попеременно переключаться между ними (каждому потоку, как упоминалось, выделяется какое-то время выполнения), обрабатывая по одной задаче из каждого потока.
Если у вас одноядерный процессор, настоящего одновременного выполнения у вас не будет. Процессор просто будет переключаться между потоками, это настолько быстро, что вам кажется что программы на вашем компьютере выполняются в одно и то же время.
Кстати, вот состояния потоков:
- создание (new) - поток находится в процессе создания
- исполнение (running) - инструкции потока (ваш код, по сути) исполняет процессор
- ожидание (waiting) - поток ожидает какого-то события (например, завершение операции ввода-вывода); такое состояние называют заблокированным, а поток - прекращенным
- готовность (ready) - поток ожидаем, что планировщик переключит процессор на него, при этом, он уже имеет все необходимые ему ресурсы, кроме процессорного времени
- завершение (terminated) - поток прекратил свое исполнение
Кратко пробежимся. Процесс - программа. У каждого процесса есть главный исполняемый поток, в этом потоке содержутся инструкции для выполнения процессором - ваш код. Но иногда, одного потока вам недостаточно, вы можете создать еще один, то есть еще один фрагмент, кусок кода, который будет выполнен.
Разберем один из примеров использования потоков. Создадим JavaFx приложение, которое по заданному юзернейму будет искать в импровизированной БД информацию о юзере.
В методе, где мы по ключу достаем информацию, сделаем задержку 3 секунды, потом вызвав этот метод в единственном потоке нашего приложения, получим блокировку.
Чтобы это исправить, нужно поместить вызов метода, в котором происходит блокрирующая операция(операция, при которой приходится ждать какое-то количество времени до получения ответа, результата) в отдельный новый поток.
new Thread(() -> { if (database.isUserInDb(username)) { resultText.setText(database.getUserInfoByUsername(username)); } else { resultText.setText("Такого юзера нет в БД"); } }).start();
Вот вам и многопоточность. Пока один поток занимается обработкой графического интерфейса, другой - ходит в БД, блокируется(ждет), выдает результат.