сеньорчикОткрыть в Telegram
← все вопросывопросы для собеседований · Многопоточность в Java

Вопросы по многопоточности в Java на собеседовании

Многопоточность в Java любят на собесах именно потому, что здесь нельзя ответить заученным определением. Спросят, что произойдёт с полем без volatile, и сразу видно, читал человек про модель памяти или запомнил слово.

83 вопросов в банке·5 подтем·ниже разбор 9

Что спрашивают

Из чего состоит тема

Так тема разложена в тренажёре: движок ведёт прогресс по каждой подтеме отдельно и возвращает те, где вы ошибаетесь.

Разборы подтем

Конспект по каждой: что это, как отвечать вслух, на чём валятся, плюс вопросы для самопроверки.

Примеры вопросов с разбором

  1. #executors_futures1 / 9
    Зачем использовать ExecutorService вместо new Thread на каждую задачу?
    A)Переиспользует пул потоков и управляет их жизненным циклом
    B)Он создаёт по одному новому потоку на каждую задачу, но делает это чуть быстрее ручного new Thread
    C)Он выполняет все задачи строго последовательно в одном потоке, обеспечивая отсутствие гонок
    D)Он автоматически делает весь переданный ему код потокобезопасным без синхронизации со стороны разработчика
    показать ответ и разбор
    +A)Переиспользует пул потоков и управляет их жизненным циклом

    // разбор: ExecutorService отделяет ЗАДАЧИ от ПОТОКОВ: держит пул переиспользуемых потоков, ставит задачи в очередь, распределяет их, возвращает Future с результатом и даёт управляемое завершение (shutdown). Это убирает дороговизну создания потока на каждую задачу и хаос ручного управления. Создают через Executors.* или напрямую ThreadPoolExecutor (тонкая настройка). Потокобезопасность самих задач — всё ещё забота разработчика.

  2. #locks_tools2 / 9
    Чем ReentrantLock даёт больше возможностей, чем synchronized?
    A)tryLock, прерываемость, справедливость, несколько Condition
    B)Он работает быстрее synchronized, поэтому synchronized в новом коде устарел
    C)Он снимает блокировку автоматически при выходе из метода, так что вызывать unlock() вручную не нужно
    D)Он один обеспечивает видимость изменений между потоками, чего synchronized делать не умеет
    показать ответ и разбор
    +A)tryLock, прерываемость, справедливость, несколько Condition

    // разбор: ReentrantLock — явная блокировка с гибкостью, которой нет у synchronized: неблокирующая попытка tryLock() и с таймаутом (профилактика deadlock), прерываемое ожидание lockInterruptibly(), опциональная справедливость (FIFO), несколько Condition на один лок. Плата — надо вручную unlock() в finally, иначе лок утечёт. synchronized проще и достаточно в большинстве случаев; Lock берут, когда нужны эти фичи.

  3. #modern_concurrency3 / 9
    Что такое виртуальные потоки (virtual threads, Java 21)?
    A)Лёгкие потоки, планируемые JVM поверх немногих потоков-носителей
    B)Это обычные потоки ОС, которым JVM просто присвоила пониженный приоритет ради экономии памяти
    C)Это отдельные процессы операционной системы, которыми управляет не JVM, а планировщик ядра ОС
    D)Это потоки, выполняющие лишь не-блокирующий код: блокирующий вызов внутри них запрещён
    показать ответ и разбор
    +A)Лёгкие потоки, планируемые JVM поверх немногих потоков-носителей

    // разбор: Виртуальные потоки (Project Loom, финализированы в Java 21) — очень лёгкие потоки, которыми управляет JVM, мультиплексируя их на небольшое число платформенных потоков-носителей (carrier). Их можно создавать миллионами. При блокирующей операции виртуальный поток «отмонтируется» от носителя, освобождая его для других — поэтому блокирующий стиль кода снова дёшев. Создаются через Thread.ofVirtual() или Executors.newVirtualThreadPerTaskExecutor().

  4. #synchronization4 / 9
    Что такое состояние гонки (race condition)?
    A)Результат зависит от непредсказуемого порядка доступа потоков к общим данным
    B)Ситуация, когда два потока стартуют одновременно и соревнуются, кто первым захватит процессорное ядро
    C)Ошибка, при которой поток бесконечно ждёт монитор, уже захваченный другим потоком навсегда
    D)Замедление программы из-за того, что потоки по очереди выполняют один и тот же участок кода
    показать ответ и разбор
    +A)Результат зависит от непредсказуемого порядка доступа потоков к общим данным

    // разбор: Race condition — когда корректность зависит от НЕДЕТЕРМИНИРОВАННОГО порядка, в котором потоки читают/пишут общие данные без синхронизации. Классика — два потока делают counter++ (чтение-инкремент-запись), и одно обновление теряется (lost update). Такие баги плавающие и трудно воспроизводимые. Лечат синхронизацией (synchronized, Lock), атомиками или неизменяемостью/изоляцией данных по потокам.

  5. #threads_basics5 / 9
    Чем отличается вызов thread.start() от thread.run()?
    A)start() запускает новый поток; run() выполняется в текущем
    B)Start() и run() делают одно и то же — оба создают новый поток и выполняют его тело параллельно
    C)Run() запускает новый поток, а start() лишь помечает поток готовым к запуску, но не выполняет его
    D)Start() выполняет код синхронно в текущем потоке, а run() отдаёт его планировщику ОС в отдельный поток
    показать ответ и разбор
    +A)start() запускает новый поток; run() выполняется в текущем

    // разбор: start() просит JVM/ОС создать новый поток исполнения, который затем вызывает run(). Прямой вызов run() — это обычный вызов метода в ТЕКУЩЕМ потоке, никакой параллельности. Частая ошибка новичка: написать run() вместо start() и удивляться, что «многопоточности нет». Повторный start() у одного объекта Thread тоже запрещён (IllegalThreadStateException).

  6. #executors_futures6 / 9
    Чем опасен Executors.newCachedThreadPool под высокой нагрузкой?
    A)Он держит ровно один поток, поэтому под нагрузкой задачи выстраиваются в бесконечную очередь и висят
    B)Он создаёт неограниченно много потоков и может исчерпать ресурсы
    C)Он заранее создаёт фиксированное большое число потоков, занимая всю память ещё до поступления задач
    D)Он не переиспользует потоки: каждый поток после одной задачи немедленно уничтожается
    показать ответ и разбор
    +B)Он создаёт неограниченно много потоков и может исчерпать ресурсы

    // разбор: newCachedThreadPool создаёт потоки по мере поступления задач без верхнего предела (и переиспользует простаивающие ~60 сек). Под резким всплеском задач он может наплодить тысячи потоков и уронить приложение (OutOfMemory, деградация планировщика). Для контролируемой нагрузки берут пул с явной верхней границей и ОГРАНИЧЕННОЙ очередью (ThreadPoolExecutor) и продуманной политикой отказа, а не «резиновый» cached.

  7. #locks_tools7 / 9
    Что делает tryLock() у ReentrantLock?
    A)Захватывает лок и ждёт его сколько угодно долго, но при этом не допускает deadlock
    B)Пытается захватить лок и сразу возвращает true/false, не блокируясь
    C)Захватывает сразу два лока атомарно, что делает вложенные блокировки безопасными
    D)Проверяет, свободен ли лок, но сам его не берёт — захват нужно потом выполнить методом lock()
    показать ответ и разбор
    +B)Пытается захватить лок и сразу возвращает true/false, не блокируясь

    // разбор: tryLock() пытается захватить лок и НЕМЕДЛЕННО возвращает результат: true (захватил — не забыть unlock в finally) или false (занят, можно сделать что-то другое). Есть вариант с таймаутом tryLock(t, unit). Это ключевой инструмент против deadlock: вместо вечного ожидания поток может отступить, освободить уже взятые локи и повторить попытку. С synchronized так нельзя — там ожидание всегда блокирующее.

  8. #modern_concurrency8 / 9
    Чем платформенный поток отличается от виртуального?
    A)Платформенный поток не получится заблокировать, а виртуальный можно — в остальном они идентичны по цене
    B)Платформенный — обёртка над потоком ОС (дорогой); виртуальный — управляется JVM (дешёвый)
    C)Виртуальный поток быстрее платформенного на вычислениях за счёт компиляции в нативный код
    D)Платформенные потоки появились в Java 21, а виртуальные существуют с самых первых версий языка
    показать ответ и разбор
    +B)Платформенный — обёртка над потоком ОС (дорогой); виртуальный — управляется JVM (дешёвый)

    // разбор: Платформенный поток — тонкая обёртка над потоком ОС (1:1), дорогой по памяти (стек ~1МБ) и переключению; их держат сотнями. Виртуальный — сущность JVM, которую планировщик Loom мультиплексирует на пул носителей (M:N), дешёвая и многочисленная. Для CPU-задач разницы в скорости нет (упираемся в ядра); выигрыш виртуальных — в МАССОВОЙ конкурентности блокирующих операций (много одновременных запросов, ожидающих I/O).

  9. #synchronization9 / 9
    Что гарантирует блок synchronized?
    A)Только взаимное исключение; видимость записей другим потокам он никак не обеспечивает
    B)Взаимное исключение и видимость изменений между потоками
    C)Только атомарность отдельных операций внутри блока, но два потока могут входить в него одновременно
    D)Что заблокированный участок выполнится быстрее, так как JVM отдаёт ему всё процессорное время
    показать ответ и разбор
    +B)Взаимное исключение и видимость изменений между потоками

    // разбор: synchronized захватывает МОНИТОР объекта: в защищённый участок в каждый момент входит только один поток (взаимное исключение). Плюс он устанавливает отношение happens-before — изменения, сделанные до выхода из монитора, ВИДНЫ потоку, который затем этот монитор захватит. То есть synchronized решает сразу и атомарность критической секции, и видимость. Монитор реентерабельный (один поток может войти повторно).

это 9 из 83

Ещё 74 вопросов по теме — в тренажёре, с движком повторения

Прочитать разбор и ответить самому — разные навыки. В Сеньорчике вопросы идут сессиями, а движок возвращает подтемы, где вы ошибаетесь, пока они не начнут отскакивать. Бесплатно, лимит по энергии.

Частые вопросы