Будет ли соблюдаться очередь при входе нескольких потоков в synchronized блок
Что проверяют
Знаете ли вы, что встроенная блокировка в Java несправедливая. Вопрос с ловушкой: интуитивно кажется, что потоки входят в порядке очереди, но гарантии такой нет.
Как отвечать
Правильный ответ: порядок не гарантируется. synchronized обеспечивает взаимное исключение, то есть внутрь блока попадает только один поток, но выбор следующего остаётся за виртуальной машиной и планировщиком операционной системы. Ожидающие потоки не образуют честной очереди, и поток, пришедший позже, вполне может войти раньше того, кто ждал дольше.
Более того, несправедливость сделана намеренно: передача блокировки только что освободившему её потоку дешевле, чем пробуждение спящего, и общая пропускная способность выше. Плата за это возможное голодание отдельного потока под высокой конкуренцией.
Если порядок нужен, берут явную блокировку:
private final ReentrantLock lock = new ReentrantLock(true); // справедливый режимСправедливый режим выдаёт блокировку в порядке ожидания, но заметно снижает пропускную способность, поэтому включают его осознанно. Когда нужен именно порядок обработки, чаще берут не блокировку, а очередь: ArrayBlockingQueue или LinkedBlockingQueue с одним обработчиком, и тогда порядок задан структурой данных, а не надеждой на планировщик.
Стоит добавить, что synchronized даёт не только исключение, но и видимость: выход из блока публикует изменения, вход их видит.
Где спотыкаются
Уверенно отвечают «да, очередь соблюдается», повторяя частый миф.
Считают, что ArrayBlockingQueue справедлива по умолчанию: у неё тоже есть флаг честности, и по умолчанию он выключен.
Забывают, что блокировка привязана к объекту: два потока, синхронизированные по разным объектам, друг друга не ждут вовсе.