You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何我的LeetCode多线程FizzBuzz解决方案每次运行输出不同?

多线程FizzBuzz同步逻辑调试问题

我正在调试多线程FizzBuzz问题的解决方案,代码逻辑看似安全,但在LeetCode平台上,当n=50时每次运行输出结果都不一致,本地测试无法稳定复现该问题。

我设定的预期不变量为:

  • 仅id与turn匹配的线程可执行
  • 其他所有线程阻塞在各自的Semaphore上
  • 打印完成后,活跃线程仅释放下一个预期的线程

我需要以下两种结果之一:

  1. 打破该不变量的具体线程执行交错场景
  2. 解释该不变量本身为何不成立

目前我不需要替代实现,仅希望理解此版本同步逻辑中的问题。我特意设定的一个设计约束是:每个输出步骤仅计算一次下一个动作,而非在每个工作线程中独立重新判断整除性。主方法会创建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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.01 16:24:49