Java: Продвинутое использование
Теория: Многопоточность в Java
Потоки в Java
В Java каждый процесс, который запускается в операционной системе, имеет хотя бы один выполняющийся поток. Этот поток называется "главным потоком" и его выполнение начинается с метода main(). Метод main() является точкой входа в программу, и именно с него начинается выполнение всех операций, которые определены в коде.
Когда программа запускается, JVM создает главный поток, который отвечает за выполнение кода, находящегося в методе main(). Однако, в современных приложениях часто возникает необходимость выполнять несколько задач одновременно, что приводит к необходимости создания дополнительных потоков. Эти дополнительные потоки называются "побочными потоками". Побочные потоки могут быть созданы в заданных программистом местах кода, что позволяет выполнять параллельные задачи. Например, если ваша программа должна обрабатывать данные, загружать файлы или выполнять сетевые запросы, вы можете создать побочные потоки для выполнения этих задач, не блокируя главный поток.
Создание потоков в Java
В Java существует несколько способов создания потоков, каждый из которых имеет свои особенности и подходит для различных сценариев. Рассмотрим основные методы создания потоков
Реализация интерфейса Runnable
Один из способов создания потока — это реализация интерфейса Runnable. В этом случае вы создаете объект класса, реализующего Runnable, и передаете его в конструктор класса Thread. Это позволяет вам использовать один и тот же объект Runnable в нескольких потоках, что может быть полезно для выполнения одной и той же задачи параллельно.
Результат выполнения может быть следующий:
Порядок вывода сообщений на экран не гарантирован. Когда мы запустили новый поток с помощью метода start(), этот поток начинает выполняться параллельно с основным потоком. Оба потока работают независимо друг от друга, между ними нет никакой синхронизации. В результате, вы можете увидеть различные комбинации вывода
Наследование класса Thread
Еще один способ — это наследование класса Thread. Для этого необходимо создать новый класс, который будет наследовать Thread, и переопределить его метод run(). В этом методе будет описана логика, выполняемая в потоке
При вызове метода start() создается новый поток, и выполнение кода переходит в метод run()
Потоки в программе
В Java программа завершает свою работу, когда все её потоки завершили выполнение. Однако это утверждение не относится к потокам-демонам. Потоки-демоны — это специальные потоки, которые работают в фоновом режиме и предназначены для выполнения вспомогательных задач, таких как сборка мусора или обработка событий. Они отличаются от обычных потоков тем, что их существование не препятствует завершению программы. Когда все обычные потоки завершили свою работу, JVM завершает выполнение программы, даже если потоки-демоны все еще работают.
Завершение потоков
Завершение потоков в Java — это важная тема, требующая внимательного подхода
Завершение потоков в Java — это важная тема, требующая внимательного подхода, поскольку неправильное управление потоками может привести к серьезным проблемам в работе программы. Методы Thread.stop(), Thread.suspend() и Thread.resume() считаются устаревшими и не рекомендуются к использованию. Эти методы могут вызвать непредсказуемые последствия.
Использование метода Thread.stop() завершает поток в любой момент, не давая ему возможности освободить ресурсы или завершить текущие операции. Это может привести к повреждению данных или оставлению ресурсов, таких как файловые дескрипторы, в неосвобожденном состоянии.
Использование метода Thread.suspend() может привести к блокировке потока, который не будет продолжать выполнение, пока не будет вызван Thread.resume(). Это может вызвать зависание всей программы.
Механизм прерываний
Вместо устаревших методов, Java предлагает механизм прерываний, который позволяет потокам получать сигнал о необходимости завершения своей работы. Это делается с помощью метода interrupt(), который устанавливает флаг прерывания для потока. Поток, получивший сигнал о прерывании, может проверить этот флаг и решить, когда и как завершить свою работу.
Каждый поток в Java, как наследник класса Thread, содержит метод isInterrupted(), который позволяет проверить, был ли поток прерван. Этот метод возвращает true, если поток был прерван, и false в противном случае. Это позволяет потокам самостоятельно решать, когда и как завершить свою работу, основываясь на состоянии прерывания.
Ожидание завершения с помощью join
В Java предусмотрен механизм, позволяющий одному потоку ожидать завершения выполнения другого потока. Этот механизм реализован с помощью метода join().
Когда один поток вызывает метод join() на другом потоке, вызывающий поток приостанавливает своё выполнение до тех пор, пока целевой поток не завершит свою работу. Это особенно полезно в ситуациях, когда необходимо гарантировать, что определённые задачи будут выполнены до того, как продолжится выполнение основного потока. Например, если у вас есть поток, который выполняет важные вычисления или обрабатывает данные, и вы хотите дождаться его завершения, прежде чем переходить к следующему этапу программы/
Метод join() также имеет перегруженную версию, которая принимает в качестве параметра время ожидания в миллисекундах. Это позволяет потоку ожидать завершения другого потока только в течение определённого времени. Если целевой поток завершится до истечения этого времени, вызывающий поток продолжит выполнение. Если же время ожидания истечёт, вызывающий поток продолжит выполнение, даже если целевой поток всё ещё работает. Это может быть полезно в ситуациях, когда необходимо избежать бесконечного ожидания, например, если вы хотите, чтобы программа продолжала работать, даже если один из потоков завис или выполняется слишком долго.


