Как работает красно-желто-зеленый светофор в многопоточности?
Что проверяют
Это задача на синхронизацию потоков под видом бытовой модели: светофор меняет состояние, а потоки должны реагировать на него без гонок и зависаний. От QA-инженера ждут двух вещей: объяснить механизм и рассказать, как такой код тестировать. Следом обычно спрашивают: «как вы поймаете гонку, которая воспроизводится раз в сто запусков?»
Как отвечать
Задачу дают в двух вариантах. В первом один поток переключает цвета по кругу, а потоки-машины ждут зелёного. Во втором три потока печатают «красный», «жёлтый», «зелёный» строго по очереди. Решаются они одними инструментами, пример на Java:
- общее состояние: цвет хранится в
enum, а не в строке, и читается и меняется под одной блокировкой, иначе поток увидит устаревшее значение; - ожидание события:
ReentrantLockсConditionилиsynchronizedсwait()/notifyAll(). Ждут в циклеwhile (color != GREEN) cond.await();, потому что пробуждение бывает ложным; - очерёдность: для трёх потоков удобны три
Semaphore: поток забирает своё разрешение, печатает цвет и выдаёт разрешение следующему; - одновременный старт:
CountDownLatchилиCyclicBarrier, чтобы потоки начали работу в один момент.
Затем про тестирование. Многопоточный код одним прогоном не проверяется: тест повторяют сотни раз (@RepeatedTest в JUnit 5), ставят таймауты через @Timeout, а вместо Thread.sleep ждут условия через Awaitility. Для точечной проверки гонок в Java есть jcstress. При зависании снимают дамп потоков через jstack и смотрят, кто какую блокировку держит.
Где спотыкаются
Защищают блокировкой смену цвета, но читают его без неё. Тогда поток может вообще не увидеть изменение: без volatile или блокировки видимость между потоками не гарантирована.
Пишут if вместо while при ожидании условия и notify() вместо notifyAll(). Первое ломается на ложном пробуждении, второе будит не тот поток, и остальные ждут вечно.
Делают вывод «гонок нет» по одному зелёному прогону. Флаки в многопоточном тесте чаще сигнал о баге в коде, чем о плохом окружении, и списывать его на инфраструктуру не стоит.