来源于leetcode1195
编写一个可以从 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 实例会被如下四个线程使用:
本文附上我自己的写法加上几种在评论区看到的解法,供参考。
while循环等待满足条件时输出
class FizzBuzz {
private int n;
private int cnt = 1;
private Object lock = new Object();
public FizzBuzz(int n) {
this.n = n;
this.cnt = 1;
}
// printFizz.run() outputs "fizz".
public void fizz(Runnable printFizz) throws InterruptedException {
while (cnt <= n) {
// flag
if (cnt % 3 == 0 && cnt % 5 != 0) {
//双重检查,防止其他线程修改了cnt
//如果n=8,A在flag位置让出时间片
//此时下面number打印数字并加一,此时回到flag,第一层检查通过
//如果没有cnt<=n,第二层检查通过,将会打印9,与预期不符
synchronized (lock) {
if (cnt <= n && cnt % 3 == 0 && cnt % 5 != 0) {
printFizz.run();
cnt++;
}
}
}
}
}
// printBuzz.run() outputs "buzz".
public void buzz(Runnable printBuzz) throws InterruptedException {
while (cnt <= n) {
if (cnt % 5 == 0 && cnt % 3 != 0) {
synchronized (lock) {
if (cnt <= n && cnt % 5 == 0 && cnt % 3 != 0) {
printBuzz.run();
cnt++;
}
}
}
}
}
// printFizzBuzz.run() outputs "fizzbuzz".
public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
while (cnt <= n) {
if (cnt % 5 == 0 && cnt % 3 == 0) {
synchronized (lock) {
if (cnt <= n && cnt % 5 == 0 && cnt % 3 == 0) {
printFizzBuzz.run();
cnt++;
}
}
}
}
}
// printNumber.accept(x) outputs "x", where x is an integer.
public void number(IntConsumer printNumber) throws InterruptedException {
while (cnt <= n) {
if (cnt % 5 != 0 && cnt % 3 != 0) {
synchronized (lock) {
if (cnt <= n && cnt % 5 != 0 && cnt % 3 != 0) {
printNumber.accept(cnt);
cnt++;
}
}
}
}
}
}
直接用原子类就能解决共享变量的问题。
class FizzBuzz {
private int n;
public AtomicInteger count=new AtomicInteger(1);
public FizzBuzz(int n) {
this.n = n;
}
// printFizz.run() outputs "fizz".
public void fizz(Runnable printFizz) throws InterruptedException {
while (count.get() <= n) {
int tmp = count.get();
if (tmp<=n&&tmp % 3 == 0 && tmp % 15 != 0) {
printFizz.run();
count.getAndIncrement();
}
}
}
// printBuzz.run() outputs "buzz".
public void buzz(Runnable printBuzz) throws InterruptedException {
while (count.get() <= n) {
int tmp = count.get();
if (tmp<=n&&tmp % 5 == 0 && tmp % 15 != 0) {
printBuzz.run();
count.getAndIncrement();
}
}
}
// printFizzBuzz.run() outputs "fizzbuzz".
public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
while (count.get() <= n) {
int tmp = count.get();
if (tmp<=n&&tmp % 15 == 0) {
printFizzBuzz.run();
count.getAndIncrement();
}
}
}
// printNumber.accept(x) outputs "x", where x is an integer.
public void number(IntConsumer printNumber) throws InterruptedException {
while (count.get() <= n) {
int tmp=count.get();
if (tmp<=n&&tmp % 3 != 0 && tmp % 5 != 0) {
printNumber.accept(tmp);
count.getAndIncrement();
}
}
}
}
直接用wait-notify实现同步。如果不满足自己当前的条件就wait,否则notify其他人。这里我有一个困惑的地方,notify不会释放锁,那应该一直在while里面循环才对,但是下面代码是可以提交成功的。
class FizzBuzz {
private int n;
private int curNum=1;
public FizzBuzz(int n) {
this.n = n;
}
// printFizz.run() outputs "fizz".
public synchronized void fizz(Runnable printFizz) throws InterruptedException {
while(curNum<=n){
if(curNum%3!=0 || curNum%5==0){
wait();
continue;
}
printFizz.run();
curNum++;
notifyAll();
}
}
// printBuzz.run() outputs "buzz".
public synchronized void buzz(Runnable printBuzz) throws InterruptedException {
while(curNum<=n){
if(curNum%5!=0 || curNum%3==0){
wait();
continue;
}
printBuzz.run();
curNum++;
notifyAll();
}
}
// printFizzBuzz.run() outputs "fizzbuzz".
public synchronized void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
while(curNum<=n){
if(curNum%15 != 0){
wait();
continue;
}
printFizzBuzz.run();
curNum++;
notifyAll();
}
}
// printNumber.accept(x) outputs "x", where x is an integer.
public synchronized void number(IntConsumer printNumber) throws InterruptedException {
while(curNum<=n){
if(curNum%3==0 || curNum%5==0){
wait();
continue;
}
printNumber.accept(curNum);
curNum++;
notifyAll();
}
}
}
用了四个信号量,这我可真想不到。
class FizzBuzz {
private int n;
private Semaphore fizzSem = new Semaphore(0);
private Semaphore buzzSem = new Semaphore(0);
private Semaphore fizzBuzzSem = new Semaphore(0);
private Semaphore numberBuSem = new Semaphore(1);
public FizzBuzz(int n) {
this.n = n;
}
public void releaseSemaphoreByNum(int i){
// 根据数字决定释放哪一把锁
if((i+1)%3==0 && (i+1)%5==0){
// 能被3和5 整除
fizzBuzzSem.release();
}else if((i+1)%3==0){
// 能被3整除
fizzSem.release();
}else if((i+1)%5==0){
// 能被5整除
buzzSem.release();
}else{
// 不能被3和5整除
numberBuSem.release();
}
}
// printFizz.run() outputs "fizz".
public void fizz(Runnable printFizz) throws InterruptedException {
for(int i=0;i<=n;i++){
if(i%3==0 && i%5!=0){
fizzSem.acquire();
printFizz.run();
releaseSemaphoreByNum(i);
}
}
}
// printBuzz.run() outputs "buzz".
public void buzz(Runnable printBuzz) throws InterruptedException {
for(int i=1;i<=n;i++){
if(i%5==0 && i%3!=0){
buzzSem.acquire();
printBuzz.run();
releaseSemaphoreByNum(i);
}
}
}
// printFizzBuzz.run() outputs "fizzbuzz".
public void fizzbuzz(Runnable printFizzBuzz)throws InterruptedException {
for(int i=1;i<=n;i++){
if(i%5==0 && i%3==0){
fizzBuzzSem.acquire();
printFizzBuzz.run();
releaseSemaphoreByNum(i);
}
}
}
// printNumber.accept(x) outputs "x", where x is an integer.
public void number(IntConsumer printNumber) throws InterruptedException {
for(int i=1;i<=n;i++){
if(i%3!=0 && i%5!=0){
numberBuSem.acquire(); // 获得一把锁,锁--
printNumber.accept(i); //打印数字
releaseSemaphoreByNum(i);
}
}
}
}