Писали ли вы многопоточные алгоритмы?
Что проверяют
Отличаете ли вы использование готовых примитивов от написания конкурентного алгоритма и понимаете ли, что здесь ошибки не воспроизводятся по требованию. Заодно смотрят на трезвость: многопоточность нужна не всегда, и знание, когда её не делать, ценится не меньше.
Как отвечать
Начните с задачи, а не с инструмента: что распараллеливали, почему это вообще ускоряется, где была общая изменяемая память. Дальше по уровням:
- пул задач через
ExecutorService, сбор результатов черезFutureилиCompletableFuture, а не через общий список и ожидание в цикле; - готовые потокобезопасные структуры:
ConcurrentHashMap, очереди изjava.util.concurrent, счётчики наLongAdderиAtomicLong; - синхронизация только там, где без общего состояния не обойтись:
synchronizedилиReentrantLock, критическая секция максимально короткая; - разделяемые данные либо неизменяемые, либо защищены явно, а поле-флаг остановки помечается
volatile; - рекурсивное дробление через
ForkJoinPoolиRecursiveTask, если работа делится естественно.
Обязательно скажите про проверку: прогон под нагрузкой, запуск с числом потоков больше числа ядер, поиск взаимных блокировок, отдельный стресс-тест на гонки. И назовите честную границу: последовательная часть работы никуда не девается, поэтому линейного роста от числа ядер не бывает.
Где спотыкаются
Называют многопоточностью вызов параллельного потока данных на коллекции. Это допустимо, но пул там общий на приложение, и одна долгая задача с блокирующим вводом и выводом тормозит всех остальных.
Второе: приносят точные проценты ускорения из резюме. Фраза про сорок процентов без описания условий замера звучит шаблонно, и первый же вопрос про профиль нагрузки её вскрывает.
Третье: считают, что потокобезопасная коллекция делает потокобезопасным код вокруг неё. Проверка и запись двумя отдельными вызовами это гонка даже на ConcurrentHashMap, для неё есть атомарные compute и putIfAbsent.