Почему многопоточное решение LeetCode Fizz Buzz выдает разные выходные данные при каждом запуске?JAVA

Программисты JAVA общаются здесь
Anonymous
Почему многопоточное решение LeetCode Fizz Buzz выдает разные выходные данные при каждом запуске?

Сообщение Anonymous »

Я отлаживаю решение для LeetCode Fizz Buzz Multithreaded:
https://leetcode.com/problems/fizz-buzz-multithreaded/
Приведенная ниже реализация кажется мне логически безопасной, но в LeetCode она выдает разные выходные данные при n = 50. Локально мои тесты не воспроизводят проблему последовательно.
Мой предполагаемый инвариант:
  • только поток, чей идентификатор соответствует ходу, может продолжить работу
  • все остальные потоки блокируются на своем семафоре
  • после печати активный поток освобождает ровно следующий ожидаемый поток thread
Поэтому я ищу одну из двух вещей:
  • конкретное чередование, которое нарушает этот инвариант, или
  • объяснение того, почему сам инвариант ложный
Я не ищу альтернативные реализации тем не менее, я специально хочу понять, что не так с логикой синхронизации в этой версии.
Одним из ограничений дизайна, которые я выбрал намеренно, было вычисление следующего действия только один раз за шаг вывода, а не повторная оценка делимости независимо в каждом рабочем процессе.
Основной метод предположительно создает 4 потока, которые вызывают один раз один из общедоступных методов.
class FizzBuzz {
private final Semaphore sFizz = new Semaphore(0, false);
private final Semaphore sBuzz = new Semaphore(0, false);
private final Semaphore sFizzBuzz = new Semaphore(0, false);
private final Semaphore sNumber = new Semaphore(0, false);
private volatile int index = 1;
private final int max;
private volatile Id turn;
private final Object mutex = new Object();

public FizzBuzz(int n) {
this.max = n;
turn = calculateTurn(index);
}

private enum Id {
FIZZ, BUZZ, FIZZBUZZ, NUMBER
}

private Integer markReady(Id id) throws InterruptedException {
if (id != turn) {
switch (id) {
case NUMBER -> sNumber.acquire();
case BUZZ -> sBuzz.acquire();
case FIZZ -> sFizz.acquire();
case FIZZBUZZ -> sFizzBuzz.acquire();
}
}

synchronized (mutex) {
if (index > max) {
return null;
}
int turnIndex = index++;
if (index sFizzBuzz.release();
case FIZZ -> sFizz.release();
case BUZZ -> sBuzz.release();
case NUMBER -> sNumber.release();
}
}
}

// printFizz.run() outputs "fizz".
public void fizz(Runnable printFizz) throws InterruptedException {
while (index

Вернуться в «JAVA»