编写多线程FizzBuzz测试用例

5
我正在解决来自这个链接的LeetCode问题:https://leetcode.com/problems/fizz-buzz-multithreaded/
基本上,我正在编写一个FizzBuzz类,它实现标准的FizzBuzz,但使用4个同时运行的线程,每个线程调用不同的方法。(一个线程调用fizzbuzz,一个调用fizz,一个调用buzz,一个调用数字)。这是我的解决方案,已经被接受:
class FizzBuzz {
     private int n;
     int num = 1; // FizzBuzz starts at 1.
     public FizzBuzz(int n) {
         this.n = n;
     }

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

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

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



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

我的问题是我在IDE中模拟这种情况时遇到了困难。

这是我的主方法:

    public class FizzBuzzMain {
        public static void main(String[] args) {
            FizzBuzz fizzBuzz = new FizzBuzz(15);
            Runnable printFizzBuzz = new PrintFizzBuzz();
            Runnable printFizz = new PrintFizz();
            Runnable printBuzz = new PrintBuzz();
            
            Thread t1 = new Thread(printFizzBuzz);
            Thread t2 = new Thread(printFizz);
            Thread t3 = new Thread(printBuzz);
            IntConsumer printNumber = new PrintNumber();
            
            t1.start();
            t2.start();
            t3.start();
            
            try {
                fizzBuzz.number(printNumber);
                fizzBuzz.fizz(printFizz);
                fizzBuzz.buzz(printBuzz);
                fizzBuzz.buzz(printFizzBuzz);
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
    } 
 

我想要用IntConsumer初始化第四个线程(这是问题中给出的),这样我就可以执行t4,但我不确定该如何做。显然,我的主方法在某个时间点停止运行,因为所有线程都进入等待状态,没有人唤醒它们(这里缺少t4)。
非常感谢任何帮助!
3个回答

5
您可以按照以下方式进行操作:
public static void main(String[] args) {
    FizzBuzz fizzBuzz = new FizzBuzz(15);

    Runnable printFizz = () -> System.out.println("fizz");
    Runnable printBuzz = () -> System.out.println("buzz");
    Runnable printFizzBuzz = () -> System.out.println("fizzbuzz");
    IntConsumer printNumber = number -> System.out.println(number);

    Thread threadA = new Thread(() -> {
        try {
            fizzBuzz.fizz(printFizz);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });

    Thread threadB = new Thread(() -> {
        try {
            fizzBuzz.buzz(printBuzz);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });

    Thread threadC = new Thread(() -> {
        try {
            fizzBuzz.fizzbuzz(printFizzBuzz);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });

    Thread threadD = new Thread(() -> {
        try {
            fizzBuzz.number(printNumber);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });

    threadA.start();
    threadB.start();
    threadC.start();
    threadD.start();
}

解决方案可以使用Semaphore和AtomicInteger来实现并发,代码如下:

public class FizzBuzz {

private int n;
private Semaphore lock;
private AtomicInteger counter;

public FizzBuzz(int n) {
    this.n = n;
    this.lock = new Semaphore(1);
    this.counter = new AtomicInteger(1);
}

// printFizz.run() outputs "fizz".
public void fizz(Runnable printFizz) throws InterruptedException {
    int step = n/3 - n/15;
    int i = 0;
    while (i < step) {
        lock.acquire();
        if (counter.get() % 3 == 0 && counter.get() % 15 != 0) {
            printFizz.run();
            counter.incrementAndGet();
            i++;
        }
        lock.release();
    }
}

// printBuzz.run() outputs "buzz".
public void buzz(Runnable printBuzz) throws InterruptedException {
    int step = n/5 - n/15;
    int i = 0;
    while (i < step) {
        lock.acquire();
        if (counter.get() % 5 == 0 && counter.get() % 15 != 0) {
            printBuzz.run();
            counter.incrementAndGet();
            i++;
        }
        lock.release();
    }
}

// printFizzBuzz.run() outputs "fizzbuzz".
public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
    int step = n/15;
    int i = 0;
    while (i < step) {
        lock.acquire();
        if (counter.get() % 15 == 0) {
            printFizzBuzz.run();
            counter.incrementAndGet();
            i++;
        }
        lock.release();
    }
}

// printNumber.accept(x) outputs "x", where x is an integer.
public void number(IntConsumer printNumber) throws InterruptedException {
    int step = n - n/3 - n/5 + n/15;
    int i = 0;
    while (i < step) {
        lock.acquire();
        if (counter.get() % 3 != 0 && counter.get() % 5 != 0) {
            printNumber.accept(counter.get());
            counter.incrementAndGet();
            i++;
        }
        lock.release();
    }
}
}

3

我不确定我们该如何解决您的IDE问题。但我想在这里我们可以使用Semaphore.

这将通过:

class FizzBuzz {
    private int n;
    private Semaphore semNumber;
    private Semaphore semFizz;
    private Semaphore semBuzz;
    private Semaphore semFizzBuzz;

    public FizzBuzz(int n) {
        this.n = n;
        semNumber = new Semaphore(1);
        semFizz = new Semaphore(0);
        semBuzz = new Semaphore(0);
        semFizzBuzz = new Semaphore(0);
    }

    public void fizz(Runnable printFizz) throws InterruptedException {
        for (int counter = 3; counter <= n; counter += 3) {
            semFizz.acquire();
            printFizz.run();

            if ((counter + 3) % 5 == 0) {
                counter += 3;
            }

            semNumber.release();
        }
    }

    public void buzz(Runnable printBuzz) throws InterruptedException {
        for (int counter = 5; counter <= n; counter += 5) {
            semBuzz.acquire();
            printBuzz.run();

            if ((counter + 5) % 3 == 0) {
                counter += 5;
            }

            semNumber.release();
        }
    }

    public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
        for (int counter = 15; counter <= n; counter += 15) {
            semFizzBuzz.acquire();
            printFizzBuzz.run();
            semNumber.release();
        }
    }

    public void number(IntConsumer printNumber) throws InterruptedException {
        for (int counter = 1; counter <= n; counter++) {
            semNumber.acquire();

            if (counter % 15 == 0) {
                semFizzBuzz.release();

            } else if (counter % 5 == 0) {
                semBuzz.release();

            } else if (counter % 3 == 0) {
                semFizz.release();

            } else {
                printNumber.accept(counter);
                semNumber.release();
            }
        }
    }
}
  • 在您的IDE中,请确保导入java.util.concurrent

参考资料

  • 如需了解更多详细信息,可以查看讨论板块。那里有许多被接受的解决方案,包括各种语言和解释,高效的算法,以及渐近的时间/空间复杂度分析1, 2

1
谢谢!您介意为主方法提供4个线程来在本地测试吗? - lovprogramming

3

我改了你的类,这样你就可以在你的IDE中运行它,然后实现Emma提供的解决方案:

abstract class FizzBuzzRunner implements Runnable {

    protected FizzBuzz fizzBuzz;

    public FizzBuzzRunner(FizzBuzz fizzBuzz) {
        this.fizzBuzz = fizzBuzz;
    }

    abstract protected void print();
}

class PrintFizz extends FizzBuzzRunner {

    public PrintFizz(FizzBuzz fizzBuzz) {
        super(fizzBuzz);
    }

    @Override
    public void run() {
        try {
            this.fizzBuzz.fizz(this);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void print() {
        System.out.println("fizz");
    }
}

class PrintBuzz extends FizzBuzzRunner {

    public PrintBuzz(FizzBuzz fizzBuzz) {
        super(fizzBuzz);
    }

    @Override
    public void run() {
        try {
            this.fizzBuzz.buzz(this);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void print() {
        System.out.println("buzz");
    }
}

class PrintFizzBuzz extends FizzBuzzRunner {

    public PrintFizzBuzz(FizzBuzz fizzBuzz) {
        super(fizzBuzz);
    }

    @Override
    public void run() {
        try {
            this.fizzBuzz.fizzbuzz(this);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void print() {
        System.out.println("fizzbuzz");
    }
}

class IntConsumer extends PrintFizzBuzz {

    public IntConsumer(FizzBuzz fizzBuzz) {
        super(fizzBuzz);
    }

    @Override
    public void run() {
        try {
            this.fizzBuzz.number(this);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void accept(int n) {
        System.out.println(n);
    }
}

主要内容的微小更改:

public class Main {
public static void main(String[] args) {
    FizzBuzz fizzBuzz = new FizzBuzz(15);
    Runnable printFizzBuzz = new PrintFizzBuzz(fizzBuzz);
    Runnable printFizz = new PrintFizz(fizzBuzz);
    Runnable printBuzz = new PrintBuzz(fizzBuzz);
    Runnable printNumber = new IntConsumer(fizzBuzz);

    Thread t1 = new Thread(printFizzBuzz);
    Thread t2 = new Thread(printFizz);
    Thread t3 = new Thread(printBuzz);
    Thread t4 = new Thread(printNumber);

    t1.start();
    t2.start();
    t3.start();
    t4.start();
}
}

最后是FizzBuzz类:

class FizzBuzz {
private int n;
private Semaphore semNumber;
private Semaphore semFizz;
private Semaphore semBuzz;
private Semaphore semFizzBuzz;

public FizzBuzz(int n) {
    this.n = n;
    semNumber = new Semaphore(1);
    semFizz = new Semaphore(0);
    semBuzz = new Semaphore(0);
    semFizzBuzz = new Semaphore(0);
}

// printFizz.print() outputs "fizz".
public void fizz(FizzBuzzRunner printFizz) throws InterruptedException {
    for (int counter = 3; counter <= n; counter += 3) {
        semFizz.acquire();
        printFizz.print();

        if ((counter + 3) % 5 == 0) {
            counter += 3;
        }

        semNumber.release();
    }
}

// printBuzz.print() outputs "buzz".
public void buzz(FizzBuzzRunner printBuzz) throws InterruptedException {
    for (int counter = 5; counter <= n; counter += 5) {
        semBuzz.acquire();
        printBuzz.print();

        if ((counter + 5) % 3 == 0) {
            counter += 5;
        }

        semNumber.release();
    }
}

// printFizzBuzz.print() outputs "fizzbuzz".
public void fizzbuzz(FizzBuzzRunner printFizzBuzz) throws InterruptedException {
    for (int counter = 15; counter <= n; counter += 15) {
        semFizzBuzz.acquire();
        printFizzBuzz.print();
        semNumber.release();
    }
}

// printNumber.accept(x) outputs "x", where x is an integer.
public void number(IntConsumer printNumber) throws InterruptedException {
    for (int counter = 1; counter <= n; counter++) {
        semNumber.acquire();

        if (counter % 15 == 0) {
            semFizzBuzz.release();

        } else if (counter % 5 == 0) {
            semBuzz.release();

        } else if (counter % 3 == 0) {
            semFizz.release();

        } else {
            printNumber.accept(counter);
            semNumber.release();
        }
    }
}
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接