四个线程交替打印字符串

来源于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 实例会被如下四个线程使用:

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

本文附上我自己的写法加上几种在评论区看到的解法,供参考。

我的解法

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++;
                    }
                }
            }
        }
    }
}

解法2:AtomicInteger

直接用原子类就能解决共享变量的问题。

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实现同步。如果不满足自己当前的条件就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); 
            }
        }
    }
}

你可能感兴趣的:(多线程,多线程,leetcode,锁)