Частые вопросы на собеседовании Senior Java
На Senior спрашивают не определения, а границы и trade-off'ы: почему parallelStream душит общий пул, когда брать свой ForkJoinPool, а когда virtual threads, чем shared Random проигрывает ThreadLocalRandom. Ниже — такие вопросы с ответами (многопоточность, Java Core, тесты) и разборы Senior-собесов по компаниям — большинство из них в JavaJub Pro.
Вопросы с ответами
Главный плюс constructor injection в тестах?
Ключевой плюс constructor injection для юнит-тестов — независимость от Spring-контекста: объект создаётся обычным new с переданными моками, без подъёма контейнера, что радикально ускоряет тысячи тестов. @MockBean как раз ТРЕБУЕТ контекста (это медленный интеграционный путь), final-поля не ускоряют Mockito, а возможность менять зависимости сеттерами — это минус field/setter injection, а не плюс конструктора.
полный разбор →Зачем RandomGenerator вместо Random?
RandomGenerator (JEP 356, Java 17) — это общий интерфейс для разных алгоритмов ГСЧ (Xoshiro, LXM-семейство L64X/L128X и др.), позволяющий выбирать генератор по нужным свойствам (период, splittable, jumpable, leapable) через RandomGeneratorFactory. Ловушки: он не про потокобезопасность и не про криптостойкость — для секретов по-прежнему нужен SecureRandom, а Random/ThreadLocalRandom стали лишь реализациями этого интерфейса.
полный разбор →Как java.util.Random становится thread-safe?
В java.util.Random поле seed — это AtomicLong, а метод next() в цикле читает старое значение и через compareAndSet атомарно записывает новое, повторяя попытку при конфликте; так достигается формально заявленная в Javadoc потокобезопасность без блокировок. Ловушка: используется неблокирующий CAS, а не synchronized; volatile сам по себе не защищает от гонки read-modify-write, а ThreadLocal-вариант — это уже отдельный класс ThreadLocalRandom.
полный разбор →Как устроен ThreadLocalRandom?
Ключевая идея ThreadLocalRandom — отсутствие разделяемого состояния: seed лежит прямо в полях самого Thread (threadLocalRandomSeed и др.), padding-поля снижают false sharing, и обновление seed идёт без CAS и без синхронизации. Поэтому неверны варианты с общим seed под локом или CAS, а также с обёрткой ThreadLocal<Random> — это специальная JVM-оптимизация, а не обычный ThreadLocal и не один генератор на всех.
полный разбор →Когда использовать что — Random, SecureRandom, ThreadLocalRandom?
Random годится для однопоточной несекретной случайности, ThreadLocalRandom — для многопоточной несекретной (нет contention на общий seed), а SecureRandom — для криптографии (токены, пароли, соль), но он медленнее. Ловушки: общий Random в многопотоке хоть и synchronized, но создаёт contention и проигрывает ThreadLocalRandom; ThreadLocalRandom НЕ криптостоек; SecureRandom не быстрее и не замена обычному Random для произвольной случайности.
полный разбор →Когда что выбирать — свой ForkJoinPool или virtual threads?
ForkJoinPool с размером ~числа ядер — для CPU-bound (математика, парсинг, трансформации без IO), а virtual threads — для IO-bound: они дёшевы и при блокировке на JDBC/HTTP отпускают несущий поток.
полный разбор →Почему final-поля и constructor injection — стандарт Senior?
Стандарт — иммутабельность зависимостей: после создания singleton-сервиса его зависимости не должны меняться, и final это гарантирует на уровне компилятора, что и лежит в основе thread-safety. Конструктор делает зависимости явными и проверяемыми.
полный разбор →Почему .parallel() — проблема?
Главная беда parallel() — все параллельные стримы делят один общий ForkJoinPool.commonPool() на всю JVM, поэтому одна блокирующая или тяжёлая задача душит остальные, а размер пула фиксирован числом ядер. Ловушка: новый пул на каждый вызов не создаётся, порядок для корректно ассоциативных reduce сохраняется (forEachOrdered/collect это учитывают), и поэлементной сериализации нет — речь о разделяемом ресурсе и блокировках в нём.
полный разбор →Почему shared Random может быть медленнее sequential?
java.util.Random обновляет seed через CAS в цикле: при конкуренции только один поток из семи успевает, остальные крутят retry, и contention сводит на нет параллелизм. Ловушка: методы Random.next() не synchronized (там как раз lock-free CAS на AtomicLong), false sharing не главная причина, а дубликатов чисел не возникает — проигрыш именно в повторяющихся неуспешных CAS.
полный разбор →На code review найден shared java.util.Random внутри parallelStream. Какой комментарий наиболее полезен?
У shared Random есть синхронизация/CAS на общем состоянии. В параллельной обработке это может убить выигрыш от parallelStream.
полный разбор →Разборы Senior-собесов по компаниям
Что реально спрашивают на Senior в конкретных компаниях — с задачами, System Design и live-coding. Бесплатные разборы открываются сразу, остальные — в JavaJub Pro (там можно заглянуть в содержание).
Больше Senior-вопросов — в канале
Новые senior-вопросы, разборы System Design и live-coding выходят в Telegram первыми. А проверить себя можно тестами и флешкартами.