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

推荐订阅源

Security Latest
Security Latest
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Stack Overflow Blog
Stack Overflow Blog
WordPress大学
WordPress大学
N
Netflix TechBlog - Medium
GbyAI
GbyAI
云风的 BLOG
云风的 BLOG
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
宝玉的分享
宝玉的分享
博客园 - 【当耐特】
C
Cyber Attacks, Cyber Crime and Cyber Security
雷峰网
雷峰网
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
Threat Research - Cisco Blogs
NISL@THU
NISL@THU
Spread Privacy
Spread Privacy
P
Proofpoint News Feed
J
Java Code Geeks
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
MyScale Blog
MyScale Blog
T
Tor Project blog
P
Proofpoint News Feed
C
CERT Recently Published Vulnerability Notes
P
Privacy & Cybersecurity Law Blog
MongoDB | Blog
MongoDB | Blog
Simon Willison's Weblog
Simon Willison's Weblog
C
Cybersecurity and Infrastructure Security Agency CISA
L
LINUX DO - 热门话题
小众软件
小众软件
G
GRAHAM CLULEY
P
Privacy International News Feed
AWS News Blog
AWS News Blog
Know Your Adversary
Know Your Adversary
P
Palo Alto Networks Blog
人人都是产品经理
人人都是产品经理
S
Schneier on Security
Scott Helme
Scott Helme
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
B
Blog RSS Feed
T
The Exploit Database - CXSecurity.com
Recent Announcements
Recent Announcements
E
Exploit-DB.com RSS Feed
C
CXSECURITY Database RSS Feed - CXSecurity.com
U
Unit 42
The Register - Security
The Register - Security
S
Securelist
Martin Fowler
Martin Fowler
Project Zero
Project Zero
大猫的无限游戏
大猫的无限游戏
Cisco Talos Blog
Cisco Talos 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();
        }
    }
}