【问题标题】:Writing a test case for Multi-threaded FizzBuzz为多线程 FizzBu​​zz 编写测试用例
【发布时间】:2020-07-08 02:06:19
【问题描述】:

我正在通过此链接解决 LeetCode 问题:https://leetcode.com/problems/fizz-buzz-multithreaded/

基本上,我正在编写一个执行标准 fizzbuzz 的 FizzBu​​zz 类,但同时运行 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,但我不知道该怎么做。显然,我的 main 方法在某个时候停止运行,因为所有线程都进入等待状态并且没有人唤醒它们(这里缺少 t4)。

任何帮助将不胜感激!

【问题讨论】:

    标签: java multithreading thread-safety


    【解决方案1】:

    你可以这样做:

    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();
        }
    }
    }
    

    【讨论】:

      【解决方案2】:

      我修复了你的类,以便你可以在 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();
      }
      }
      

      最后是 FizzBu​​zz 类:

      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();
              }
          }
      }
      }
      

      【讨论】:

        【解决方案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

        参考文献

        • 有关其他详细信息,您可以查看Discussion Board。有很多公认的解决方案,其中包含各种languages 和解释、高效算法以及渐近的time/space 复杂性分析1, 2

        【讨论】:

        • 谢谢!您介意为 main 方法提供 4 个线程来进行本地测试吗?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-16
        • 1970-01-01
        • 1970-01-01
        • 2012-02-21
        • 2018-07-18
        相关资源
        最近更新 更多