Лекции 2025. Java. Белая / Ответы на билеты. Java
.pdf
Преимущество: Этот способ является более гибким и предпочтительным. Он позволяет вашему классу наследовать другой класс, если это необходимо, и отделяет логику задачи (
) от механизма ее выполнения в потоке
(
).
Какой способ выбрать?
Реализация интерфейса
обычно является лучшим выбосом по следующим причинам:
Гибкость наследования: Ваш класс свободен для наследования другого класса. Разделение ответственностей: Отделяет задачу (что делать) от потока (как выполнять). Один и тот же
объект может быть выполнен несколькими потоками или передан в
.
Лучший дизайн: Соответствует принципу "предпочитайте композицию наследованию". Вы "компонуете" объект
с объектом
.
Наследование от
оправдано только в редких случаях, когда вы действительно хотите модифицировать или расширить поведение самого класса
, а не просто выполнить задачу в отдельном потоке.
59. Чем различаются Thread и Runnable?
и
— это два основных элемента в Java, связанных с созданием и управлением потоками, но они представляют разные концепции:
1.
(Класс):
Что это:
— это класс, представляющий сам поток выполнения.
Объект класса
инкапсулирует всю необходимую информацию для управления потоком, такую как его состояние (новый, выполняемый, заблокированный, завершенный), приоритет, имя, и т.д.
Назначение:
Предоставляет механизм для создания нового потока операционной системы.
Управляет жизненным циклом потока (запуск через
, приостановка через
, ожидание завершения через
, прерывание через
).
Может содержать код для выполнения в потоке (если от него наследоваться и переопределить
).
Как используется:
Либо наследуется для определения кода потока.
Либо создается экземпляр
, которому передается объект
для выполнения.
2.
(Интерфейс):
Что это:
— это функциональный интерфейс, который определяет
единицу работы (задачу), предназначенную для выполнения в потоке. Он имеет всего один метод:
.
Назначение:
Абстрагировать задачу от конкретного потока, который ее будет выполнять.
Определить код, который должен быть выполнен асинхронно в отдельном потоке.
Как используется:
Класс реализует интерфейс
и помещает логику задачи в метод
.
Экземпляр этого класса (объект
) затем передается в конструктор класса
или в
для выполнения.
Ключевые различия:
Характеристика
(класс)
(интерфейс)
Сущность |
Представляет сам |
Представляет задачу или |
|
|
поток выполнения, |
единицу работы для потока |
|
|
механизм управления |
|
|
|
|
|
|
Основной метод |
(для |
|
(код задачи) |
|
запуска), |
(код |
|
|
потока, если |
|
|
|
унаследован) |
|
|
|
|
|
|
Наследование |
Класс; если |
|
Интерфейс; класс, реализующий |
|
наследуете |
, |
, может наследовать |
|
не можете |
|
другой класс |
|
наследовать другой |
|
|
|
класс |
|
|
Характеристика |
(класс) |
(интерфейс) |
|
|
|
|
|
Разделение |
Смешивает задачу с |
Четко отделяет задачу от |
|
|
механизмом потока |
механизма потока |
|
|
(при наследовании) |
|
|
|
|
|
|
Гибкость |
Менее гибкий (из-за |
Более гибкий, предпочтительный |
|
|
одиночного |
|
|
|
наследования) |
|
|
|
|
|
|
Переиспользование |
Объект |
Объект |
может быть |
задачи |
обычно выполняет |
передан нескольким потокам или |
|
|
свою задачу один раз |
|
для |
|
|
многократного выполнения (если |
|
|
|
его состояние это позволяет) |
|
|
|
|
|
API управления |
Предоставляет |
Не имеет методов для |
|
потоком |
методы для |
управления потоком напрямую |
|
|
управления потоком |
(это делает |
или |
|
(sleep, join, interrupt и |
|
) |
|
т.д.) |
|
|
|
|
|
|
Аналогия:
— это как список дел (to-do list) или инструкция к выполнению. Он описывает, что нужно сделать.
— это как рабочий (worker), который берет этот список дел и выполняет его. Один и тот же рабочий (
) может выполнить одну инструкцию (
), или можно нанять рабочего (
) специально для этой инструкции.
Почему
предпочтительнее:
Поддержка множественного наследования (через интерфейсы): Класс,
реализующий
, может свободно наследовать другой класс, что невозможно, если он уже наследует
.
Лучший дизайн (разделение ответственности):
отделяет логику задачи от логики управления потоком. Это делает код более чистым, модульным и тестируемым.
Гибкость в использовании с |
: Пулы потоков |
||
( |
) работают с объектами |
(и |
), позволяя |
эффективно управлять выполнением задач без необходимости вручную создавать и управлять объектами
.
Когда использовать
напрямую (наследование):
Только если вам нужно изменить или расширить само поведение класса
(например, добавить специфическую логику при создании потока, его завершении, или управлять его состоянием особым образом), что бывает довольно редко. В
большинстве случаев, если вы просто хотите выполнить какой-то код в отдельном потоке, реализация
— лучший выбор.
60. В чём заключается разница между методами start() и run()?
Методы
и
оба связаны с выполнением кода в потоке, но они имеют совершенно разное назначение и поведение. Оба метода принадлежат классу
.
:
Назначение: Этот метод содержит код, который фактически будет выполняться в потоке.
Если вы создаете поток путем наследования от
, вы переопределяете метод
в вашем подклассе.
Если вы создаете поток путем реализации
, вы реализуете метод
интерфейса
, и объект
вызывает этот метод
вашего
объекта.
Вызов:
Никогда не следует вызывать метод
напрямую (например,
), если вы хотите запустить код в новом потоке.
Если вы вызовете
напрямую, он просто выполнится в текущем потоке (в том же потоке, который сделал вызов), как обычный вызов метода. Новый поток создан не будет.
Поведение: Определяет "что делать" потоку.
:
Назначение: Этот метод инициирует запуск нового потока выполнения.
Вызов:
Вызывается на объекте
(например,
).
Метод
можно вызвать для объекта
только один раз. Повторный вызов
на уже запущенном или завершенном потоке приведет к исключению
.
Поведение:
1.Создает новый поток выполнения (если он еще не был создан) на уровне операционной системы.
2.Переводит состояние потока из
в
(готовый к выполнению).
3.Планировщик потоков JVM затем выбирает этот поток для выполнения, и
именно тогда вызывается метод
этого потока (или
связанного с ним
объекта) в новом, отдельном потоке.
4.Метод
возвращает управление вызывающему потоку немедленно, не дожидаясь завершения метода
. Это обеспечивает асинхронное выполнение.
Таблица сравнения:
Характеристика |
|
|
|
|
|
|
|
Цель |
Содержит код, выполняемый в |
Запускает новый поток и |
|
|
потоке. |
|
вызывает в нем метод |
|
|
|
. |
|
|
|
|
Создание нового |
Нет (если вызван напрямую). |
Да. |
|
потока? |
|
|
|
|
|
|
|
Контекст |
В текущем потоке (если вызван |
Вызывающий поток (сам |
|
выполнения |
напрямую). В новом потоке (если |
), но инициирует |
|
|
вызван через |
). |
в новом потоке. |
|
|
|
|
Когда вызывать? |
Не вызывать напрямую для |
Вызывать для запуска |
|
|
многопоточности. Реализуется/ |
потока. |
|
|
переопределяется. |
|
|
|
|
|
|
Количество |
Может быть вызван много раз |
Только один раз на объект |
|
вызовов |
(как обычный метод). |
|
. |
|
|
|
|
Возврат |
Возвращает управление после |
Возвращает управление |
|
управления |
завершения кода в |
. |
немедленно. |
|
|
|
|
Пример, иллюстрирующий разницу:
Ожидаемый (примерный) вывод:
Итог:
Для создания и запуска нового потока всегда используйте метод
. Метод
вы предоставляете (переопределяете или реализуете), чтобы
определить, какую работу будет выполнять поток, но его прямой вызов не приведет
кмногопоточному выполнению.
61.Как принудительно запустить поток?
Термин "принудительно запустить поток" может трактоваться по-разному. Давайте рассмотрим несколько сценариев:
1. Запуск потока, который еще не был запущен (состояние
):
Это стандартный сценарий. "Принудительный" запуск здесь означает просто вызов метода
.
Способ: Вызвать метод
на объекте потока
.
Это единственный корректный способ перевести поток из состояния
в
и позволить планировщику его выполнить.
2."Принудительный" запуск потока, который уже завершился (состояние
):
Это невозможно. Поток, который завершил свое выполнение (его метод
полностью отработал или был прерван и завершился), не может быть запущен повторно.
Попытка вызвать
на завершенном потоке приведет к исключению
.
Если вам нужно выполнить ту же задачу снова, вы должны создать новый экземпляр
(возможно, с тем же объектом
).
3."Принудительный" запуск потока, который находится в состоянии ожидания/ блокировки (например,
,
,
):
Прямого метода "принудительно заставить выполняться" такой поток нет.
Состояние потока управляется JVM и операционной системой в зависимости от причин блокировки (ожидание на мониторе,
, ожидание I/O, ожидание другого потока через
).
Что можно сделать (косвенно):
Для потоков в
или
из-за
: Другой поток должен вызвать
или
на том же объекте-мониторе. Это переведет ожидающий поток в состояние
или
(если монитор свободен).
Для потоков в
из-за
или
: Поток автоматически перейдет в
по истечении таймаута. Также его можно прервать (
), что заставит его выйти из
или
с
.
Для потоков в
(ожидание входа в
блок/метод):
Поток автоматически перейдет в
, как только другой поток освободит монитор.
Для потоков, ожидающих завершения другого потока через
: Поток перейдет в
, когда
завершится.
также можно прервать.
Прерывание (
): Вызов
на потоке устанавливает его флаг прерывания. Если поток находится в блокирующей операции, которая чувствительна к прерыванию (например,
,
,
, некоторые операции I/O в
), эта операция выбросит
, и поток сможет обработать это прерывание и, возможно, продолжить работу или завершиться. Если поток не находится в такой операции, он должен периодически проверять свой флаг прерывания (
) и реагировать соответствующим образом. Прерывание не "убивает" поток
принудительно, а лишь сигнализирует ему о необходимости прерваться.
4."Принудительное" возобновление выполнения потока, который был приостановлен (deprecated методы
/
):
Методы
и
являются устаревшими
(deprecated) и крайне не рекомендуются к использованию.
приостанавливал поток без освобождения им занятых мониторов,
что легко приводило к взаимным блокировкам (deadlocks).
Не используйте
и
! Вместо этого используйте более безопасные механизмы координации потоков, такие как
,
,
, или утилиты из
(например,
,
,
,
).
Вывод:
"Принудительно запустить" в смысле инициации выполнения нового или ранее не запущенного потока — это вызов
.
Завершенный поток запустить заново нельзя.
Заставить заблокированный поток немедленно выполняться напрямую нельзя; можно лишь устранить причину блокировки или прервать его.
Устаревшие методы
/
использовать нельзя.
Если под "принудительно запустить" имеется в виду гарантировать, что поток получит процессорное время немедленно, то это тоже вне прямого контроля программиста. Планировщик потоков JVM и ОС решает, какой поток будет выполняться. Можно влиять на это через приоритеты потоков (
), но это лишь подсказка планировщику, а не гарантия.
62. Что такое «монитор» в Java?
Монитор в Java (часто называемый "монитором объекта" или "внутренней блокировкой") — это механизм синхронизации, встроенный в каждый объект Java. Он используется для контроля доступа нескольких потоков к разделяемым ресурсам (обычно полям объекта или блокам кода, которые модифицируют эти поля) с целью предотвращения состояний гонки и обеспечения целостности данных.
Ключевые концепции монитора:
1. Взаимное исключение (Mutual Exclusion / Mutex):
Монитор гарантирует, что только один поток одновременно может владеть монитором (или, как говорят, "захватить блокировку" или "войти в монитор") для данного объекта.
Когда поток входит в
блок или
метод, связанный с объектом, он пытается захватить монитор этого объекта.
Если монитор свободен, поток захватывает его и продолжает выполнение кода внутри
секции.
Если монитор уже занят другим потоком, текущий поток блокируется (переходит в состояние
) и ждет, пока монитор не освободится.
Когда поток выходит из
секции (нормально или из-за исключения), он автоматически освобождает монитор, позволяя одному из ожидающих потоков его захватить.
2.Условные переменные (Condition Variables) / Механизм ожидания и уведомления:
Монитор также предоставляет механизм для координации потоков, позволяя потокам временно освобождать монитор и ожидать выполнения определенного условия, а затем быть уведомленными, когда это условие может быть выполнено.
Это реализуется с помощью методов класса
:
: Заставляет текущий поток (который должен владеть монитором объекта) освободить монитор и перейти в состояние ожидания (
или
, если
с таймаутом). Поток будет ожидать, пока другой поток не вызовет
или
на том же объектемониторе.
: Пробуждает один (произвольный) поток, ожидающий на мониторе этого объекта. Пробужденный поток не сразу получает монитор, а переходит в состояние
и конкурирует за него с другими потоками, пытающимися войти в монитор.
: Пробуждает все потоки, ожидающие на мониторе этого объекта. Все они переходят в
и конкурируют за монитор.
Важно: Методы
,
,
могут вызываться только из синхронизированного контекста (т.е. из
метода или блока),
когда текущий поток владеет монитором объекта. В противном случае будет выброшено
.
3. Связь с каждым объектом:
Каждый объект в Java (включая экземпляры классов и массивы) неявно имеет связанный с ним монитор. Вам не нужно его специально создавать.
Когда вы используете ключевое слово
, вы указываете, монитор какого объекта будет использоваться:

или
метод экземпляра:
Используется монитор текущего объекта (
).
: Используется монитор объекта
.

метод или
:
Используется монитор, связанный с объектом
для класса
(один на весь класс).
Как работает монитор (упрощенно):
Представьте, что у каждого объекта есть "комната" (критическая секция) и один "ключ" от этой комнаты (монитор).
1. Поток хочет войти в "комнату" (выполнить
код).
