为何我的LeetCode多线程FizzBuzz解决方案每次运行输出不同?
多线程FizzBuzz同步逻辑调试问题
我正在调试多线程FizzBuzz问题的解决方案,代码逻辑看似安全,但在LeetCode平台上,当n=50时每次运行输出结果都不一致,本地测试无法稳定复现该问题。
我设定的预期不变量为:
- 仅
id与turn匹配的线程可执行 - 其他所有线程阻塞在各自的Semaphore上
- 打印完成后,活跃线程仅释放下一个预期的线程
我需要以下两种结果之一:
- 打破该不变量的具体线程执行交错场景
- 解释该不变量本身为何不成立
目前我不需要替代实现,仅希望理解此版本同步逻辑中的问题。我特意设定的一个设计约束是:每个输出步骤仅计算一次下一个动作,而非在每个工作线程中独立重新判断整除性。主方法会创建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 <= max) { turn = calculateTurn(index); } return turnIndex; } } private Id calculateTurn(int _index) { if (_index % 3 == 0 && _index % 5 == 0) { return Id.FIZZBUZZ; } else if (_index % 3 == 0) { return Id.FIZZ; } else if (_index % 5 == 0) { return Id.BUZZ; } else { return Id.NUMBER; } } private void releaseWaiting(Id id) { if (id != turn) { switch (turn) { case FIZZBUZZ -> 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 <= max) { try { var num = markReady(Id.FIZZ); if (num == null) return; printFizz.run(); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { releaseWaiting(Id.FIZZ); } } releaseAll(); } // printBuzz.run() outputs "buzz". public void buzz(Runnable printBuzz) throws InterruptedException { while (index <= max) { try { var num = markReady(Id.BUZZ); if (num == null) return; printBuzz.run(); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { releaseWaiting(Id.BUZZ); } } releaseAll(); } // printFizzBuzz.run() outputs "fizzbuzz". public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException { while (index <= max) { try { var num = markReady(Id.FIZZBUZZ); if (num == null) return; printFizzBuzz.run(); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { releaseWaiting(Id.FIZZBUZZ); } } releaseAll(); } // printNumber.accept(x) outputs "x", where x is an integer. public void number(IntConsumer printNumber) throws InterruptedException { while (index <= max) { try { var num = markReady(Id.NUMBER); if (num == null) return; printNumber.accept(num); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { releaseWaiting(Id.NUMBER); } } releaseAll(); } private void releaseAll() { sFizzBuzz.release(); sFizz.release(); sBuzz.release(); sNumber.release(); } }
内容的提问来源于stack exchange,提问作者meiser
相关产品推荐
相关产品推荐

