惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Vulnerabilities – Threatpost
T
The Blog of Author Tim Ferriss
S
SegmentFault 最新的问题
D
DataBreaches.Net
博客园_首页
罗磊的独立博客
B
Blog
T
Threat Research - Cisco Blogs
C
Cisco Blogs
GbyAI
GbyAI
Engineering at Meta
Engineering at Meta
WordPress大学
WordPress大学
G
GRAHAM CLULEY
H
Help Net Security
酷 壳 – CoolShell
酷 壳 – CoolShell
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
爱范儿
爱范儿
SecWiki News
SecWiki News
T
Threatpost
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Schneier on Security
Schneier on Security
T
The Exploit Database - CXSecurity.com
Google Online Security Blog
Google Online Security Blog
T
Tor Project blog
小众软件
小众软件
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Y
Y Combinator Blog
H
Hacker News: Front Page
V
V2EX
Security Latest
Security Latest
Cloudbric
Cloudbric
Simon Willison's Weblog
Simon Willison's Weblog
Attack and Defense Labs
Attack and Defense Labs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
P
Proofpoint News Feed
博客园 - 三生石上(FineUI控件)
NISL@THU
NISL@THU
S
Secure Thoughts
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
V2EX - 技术
V2EX - 技术
Vercel News
Vercel News
P
Palo Alto Networks Blog
IT之家
IT之家
MyScale Blog
MyScale Blog
有赞技术团队
有赞技术团队
Application and Cybersecurity Blog
Application and Cybersecurity Blog
D
Docker
Google DeepMind News
Google DeepMind News
Webroot Blog
Webroot Blog

博客园 - 小纸条

模型微调 ruoyiai 启动指南 反向传播 numpy的使用 B 和 B+树 红黑树 ruoyi-vue 梯度下降法 博弈论 离散化 AcWing 907. 区间覆盖 AcWing 906. 区间分组 AcWing 908 最大不相交区间数量 AcWing 905. 区间选点 AcWing 104. 货仓选址 动态规划经典题 窗口函数 1226. 哲学家进餐 1117. H2O 生成 1116. 打印零与奇偶数 关联子查询
1195. 交替打印字符串
小纸条 · 2025-10-28 · via 博客园 - 小纸条

1195. 交替打印字符串

题目描述

编写一个可以从 1 到 n 输出代表这个数字的字符串的程序,但是:

  • 如果这个数字可以被 3 整除,输出 "fizz"。
  • 如果这个数字可以被 5 整除,输出 "buzz"。
  • 如果这个数字可以同时被 3 和 5 整除,输出 "fizzbuzz"。

例如,当 n = 15,输出: 1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, 11, fizz, 13, 14, fizzbuzz

假设有这么一个类:

class FizzBuzz {
  public FizzBuzz(int n) { ... }               // constructor
  public void fizz(printFizz) { ... }          // only output "fizz"
  public void buzz(printBuzz) { ... }          // only output "buzz"
  public void fizzbuzz(printFizzBuzz) { ... }  // only output "fizzbuzz"
  public void number(printNumber) { ... }      // only output the numbers
}

请你实现一个有四个线程的多线程版  FizzBuzz, 同一个 FizzBuzz 实例会被如下四个线程使用:

  1. 线程A将调用 fizz() 来判断是否能被 3 整除,如果可以,则输出 fizz
  2. 线程B将调用 buzz() 来判断是否能被 5 整除,如果可以,则输出 buzz
  3. 线程C将调用 fizzbuzz() 来判断是否同时能被 3 和 5 整除,如果可以,则输出 fizzbuzz
  4. 线程D将调用 number() 来实现输出既不能被 3 整除也不能被 5 整除的数字。

提示:

  • 本题已经提供了打印字符串的相关方法,如 printFizz() 等,具体方法名请参考答题模板中的注释部分。

解法

Java

class FizzBuzz {
    private int n;
    private int current = 1;

    public FizzBuzz(int n) {
        this.n = n;
    }

    // printFizz.run() outputs "fizz".
    public void fizz(Runnable printFizz) throws InterruptedException {
        while (current <= n) {
            synchronized (this) {
                if (current % 3 == 0 && current % 5 != 0) {
                    if (current <= n) {
                        printFizz.run();
                        current++;
                        notifyAll();
                    }
                } else {
                    wait();
                }
            }
        }
    }

    // printBuzz.run() outputs "buzz".
    public void buzz(Runnable printBuzz) throws InterruptedException {
        while (current <= n) {
            synchronized (this) {
                if (current % 5 == 0 && current % 3 != 0) {
                    if (current <= n) {
                        printBuzz.run();
                        current++;
                        notifyAll();
                    }
                } else {
                    wait();
                }
            }
        }
    }

    // printFizzBuzz.run() outputs "fizzbuzz".
    public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
        while (current <= n) {
            synchronized (this) {
                if (current % 15 == 0) {
                    if (current <= n) {
                        printFizzBuzz.run();
                        current++;
                        notifyAll();
                    }
                } else {
                    wait();
                }
            }
        }
    }

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void number(java.util.function.IntConsumer printNumber) throws InterruptedException {
        while (current <= n) {
            synchronized (this) {
                if (current % 3 != 0 && current % 5 != 0) {
                    if (current <= n) {
                        printNumber.accept(current);
                        current++;
                        notifyAll();
                    }
                } else {
                    wait();
                }
            }
        }
    }
}

Java

关于"唤醒所有线程以便退出"这个操作,分析如下:

是否必须取决于具体实现

  • 不是绝对必须的:当 current > n 时,理论上各线程应该已经在各自的 while(current <= n) 循环中自然退出

  • 建议保留的原因

    1. 确保及时退出:避免某些线程长时间处于 await() 状态
    2. 防止内存泄漏:确保所有线程都能正常结束,释放资源
    3. 提高健壮性:即使主流程出现异常,也能保证线程正常终止

替代方案

如果不使用 signalAll(),也可以采用以下方式:

private void signalNext() {
    if (current > n) {
        return; // 直接返回,让各线程自然退出
    } else if (current % 15 == 0) {
        fizzbuzzCondition.signal();
    } else if (current % 3 == 0) {
        fizzCondition.signal();
    } else if (current % 5 == 0) {
        buzzCondition.signal();
    } else {
        numberCondition.signal();
    }
}

结论

虽然不是严格必需,但添加 signalAll() 是一种良好的编程实践,能确保程序更可靠地退出。

class FizzBuzz {
    private int n;
    private int current = 1;
    
    private final Lock lock = new ReentrantLock();
    private final Condition fizzCondition = lock.newCondition();
    private final Condition buzzCondition = lock.newCondition();
    private final Condition fizzbuzzCondition = lock.newCondition();
    private final Condition numberCondition = lock.newCondition();

    public FizzBuzz(int n) {
        this.n = n;
    }

    public void fizz(Runnable printFizz) throws InterruptedException {
        lock.lock();
        try {
            while (current <= n) {
                if (current % 3 == 0 && current % 5 != 0) {
                    printFizz.run();
                    current++;
                    signalNext();
                } else {
                    fizzCondition.await();
                }
            }
        } finally {
            lock.unlock();
        }
    }

    public void buzz(Runnable printBuzz) throws InterruptedException {
        lock.lock();
        try {
            while (current <= n) {
                if (current % 5 == 0 && current % 3 != 0) {
                    printBuzz.run();
                    current++;
                    signalNext();
                } else {
                    buzzCondition.await();
                }
            }
        } finally {
            lock.unlock();
        }
    }

    public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
        lock.lock();
        try {
            while (current <= n) {
                if (current % 15 == 0) {
                    printFizzBuzz.run();
                    current++;
                    signalNext();
                } else {
                    fizzbuzzCondition.await();
                }
            }
        } finally {
            lock.unlock();
        }
    }

    public void number(IntConsumer printNumber) throws InterruptedException {
        lock.lock();
        try {
            while (current <= n) {
                if (current % 3 != 0 && current % 5 != 0) {
                    printNumber.accept(current);
                    current++;
                    signalNext();
                } else {
                    numberCondition.await();
                }
            }
        } finally {
            lock.unlock();
        }
    }
    
    private void signalNext() {
        if (current > n) {
            // 唤醒所有线程以便退出
            fizzCondition.signalAll();
            buzzCondition.signalAll();
            fizzbuzzCondition.signalAll();
            numberCondition.signalAll();
        } else if (current % 15 == 0) {
            fizzbuzzCondition.signal();
        } else if (current % 3 == 0) {
            fizzCondition.signal();
        } else if (current % 5 == 0) {
            buzzCondition.signal();
        } else {
            numberCondition.signal();
        }
    }
}