LeetCode多线程总结

1.LeetCode 1114 按序打印

题目:设计修改程序,以确保 two() 方法在 one() 方法之后被执行,three() 方法在 two() 方法之后被执行。

方法1:

用两个boolean变量flag1和flag2控制,当第一个线程结束,flag1=true;

线程2只有当flag1=true时,才能执行;

线程3只有当flag2=true时,才能执行。

class Foo {

    public Foo() {
        
    }

    Thread t1,t2;
    boolean flag1 = false;
    boolean flag2 = false;
    public void first(Runnable printFirst) throws InterruptedException {
        
        // printFirst.run() outputs "first". Do not change or remove this line.
        // t1 = Thread.currentThread();
        printFirst.run();
        flag1 = true;
    }

    public void second(Runnable printSecond) throws InterruptedException {
        
        // printSecond.run() outputs "second". Do not change or remove this line.
        while(!flag1){}
        printSecond.run();
        flag2 = true;
        
        
    }

    public void third(Runnable printThird) throws InterruptedException {
        
        // printThird.run() outputs "third". Do not change or remove this line.
        while(!flag2){}
            // t2.join();
        printThird.run();
    }
}

方法2:join

获取执行first的线程t1和执行second的线程t2,t1和t2初始值为null;

当获取到t1时,t2会等到t1执行完才继续执行;

同样,t3会等到t2执行完之后,才会执行。

class Foo {

    Thread t1,t2;
    public Foo() {
    }

    public void first(Runnable printFirst) throws InterruptedException {
        t1 = Thread.currentThread();
        printFirst.run();
    }

    public void second(Runnable printSecond) throws InterruptedException {
        while(t1 == null){};
        t1.join();
        t2 = Thread.currentThread();
        printSecond.run();
    }

    public void third(Runnable printThird) throws InterruptedException {
        while(t2 == null){};
        t2.join();
        printThird.run();
    }
}

方法3:wait/notifyAll

使用整型变量mark控制线程2和线程3的执行顺序,

wait/notifyAll方法需要配合synchronized代码块使用,线程1执行之后,会释放锁。

 
class Foo {

    private int mark = 0;
    private Object lock = new Object();
    public Foo() {   
    }

    public void first(Runnable printFirst) throws InterruptedException {
        synchronized(lock){
            printFirst.run();
            mark = 1;
            lock.notifyAll();
        }
    }

    public void second(Runnable printSecond) throws InterruptedException {
        synchronized(lock){
            while (mark != 1){ lock.wait(); }
            printSecond.run();
            mark = 2;
            lock.notifyAll();
        }
    }

    public void third(Runnable printThird) throws InterruptedException {
        synchronized(lock){
            while (mark != 2){ lock.wait(); }
            printThird.run();
            // lock.notifyAll();
        }
    }
}
 

方法4:CountDownLatch

用两个CountDownLatch实现,当第一个线程执行完毕,latch1减1为0,线程2就会被激活,当线程2执行完成,latch2减1为0,线程3就会被激活。

 
import java.util.concurrent.CountDownLatch;

class Foo {
    CountDownLatch latch1 = new CountDownLatch(1);
    CountDownLatch latch2 = new CountDownLatch(1);
    public Foo() {
    }

    public void first(Runnable printFirst) throws InterruptedException {
        printFirst.run();
        latch1.countDown();
    }

    public void second(Runnable printSecond) throws InterruptedException {    
        latch1.await();
        printSecond.run();
        latch2.countDown();
    }

    public void third(Runnable printThird) throws InterruptedException {
        latch2.await();
        printThird.run();
    }
}
 

 

2.Leetcode 1195 交替打印字符串

题目:编写一个可以从 1 到 n 输出代表这个数字的字符串的程序,但是:

  1. 如果这个数字可以被 3 整除,输出 "fizz"。
  2. 如果这个数字可以被 5 整除,输出 "buzz"。
  3. 如果这个数字可以同时被 3 和 5 整除,输出 "fizzbuzz"。

请你实现一个有四个线程的多线程版  FizzBuzz, 同一个 FizzBuzz 实例会被如下四个线程使用:

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

方法1:wait/notifyAll

给出一个数n,要遍历从1到n所有的数,这里用mark来表示每次遍历到的数,

4个线程之间是并行关系,

线程A的功能是找到只能被3且不能被5整除的数,否则就等待;

线程B的功能是找到只能被5且不能被3整除的数,否则就等待;

线程C的功能是找到只能被15整除的数,否则就等待;

线程D的功能是找到不能被3或5整除的数,否则就等待;

class FizzBuzz {
    private int n;
    private int mark = 1;
    private Object lock = new Object();
    public FizzBuzz(int n) {
        this.n = n;
    }

    // printFizz.run() outputs "fizz".
    public void fizz(Runnable printFizz) throws InterruptedException {
        while(mark <= n){
            synchronized(lock){
                if (mark % 3 != 0 || mark % 5 == 0){ // 不能被3整除 或 能被5整除
                    lock.wait();
                    continue;
                }else{
                    printFizz.run();
                    mark += 1;
                    lock.notifyAll();
                }
            }
        }
    }

    // printBuzz.run() outputs "buzz".
    public void buzz(Runnable printBuzz) throws InterruptedException {
        while(mark <= n){
            synchronized(lock){
                if (mark % 5 != 0 || mark % 3 == 0){ // 不能被5整除 或 能被3整除
                    lock.wait();
                    continue;
                }else{
                    printBuzz.run();
                    mark += 1;
                    lock.notifyAll();
                }
            }
        }
    }

    // printFizzBuzz.run() outputs "fizzbuzz".
    public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
        while(mark <= n){
            synchronized(lock){
                if (mark % 15 != 0){ // 能被15整除
                    lock.wait();
                    continue;
                }else{
                    printFizzBuzz.run();
                    mark += 1;
                    lock.notifyAll();
                }
            }
        }
    }

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void number(IntConsumer printNumber) throws InterruptedException {
        while(mark <= n){
            synchronized(lock){
                if (mark % 3 == 0 || mark % 5 == 0){ // 能被3整除 或 能被5整除
                    lock.wait();
                    continue;
                }else{
                    printNumber.accept(mark);
                    mark += 1;
                    lock.notifyAll();
                }
            }
        }
    }
}

方法2:semaphore

利用4个信号量实现,其中用number线程充当入口(信号量初始为1);

在number线程中先判断数字满足哪一个条件,如果满足其一,就将对应的信号量+1,

使得对应的线程获取到信号量,执行输出语句,然后释放number的信号量。

 
class FizzBuzz {
    private int n;
    private int mark = 0;
    private final Semaphore fizzSema = new Semaphore(0);
    private final Semaphore buzzSema = new Semaphore(0);
    private final Semaphore fizzbuzzSema = new Semaphore(0);
     //最开始只有number线程能够执行,其他线程的执行都受number线程的控制
    private final Semaphore numberSema = new Semaphore(1);
    public FizzBuzz(int n) {
        this.n = n;
    }

    // printFizz.run() outputs "fizz".
    public void fizz(Runnable printFizz) throws InterruptedException {
        while(true){
            fizzSema.acquire();
            if(mark > n){break;}
            printFizz.run();
            numberSema.release();
        }
    }

    // printBuzz.run() outputs "buzz".
    public void buzz(Runnable printBuzz) throws InterruptedException {
        while(true){
            buzzSema.acquire();
            if(mark > n){break;}
            printBuzz.run();
            numberSema.release();
        }
    }

    // printFizzBuzz.run() outputs "fizzbuzz".
    public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
        while(true){
            fizzbuzzSema.acquire();
            if(mark > n){break;}
            printFizzBuzz.run();
            numberSema.release();
        }
    }

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void number(IntConsumer printNumber) throws InterruptedException {
        while(mark <= n){
            numberSema.acquire();
            mark += 1;
            if(mark > n){break;}
            if(mark % 3 == 0 && mark % 5 != 0){
                fizzSema.release();
            }else if(mark % 5 == 0 && mark % 3 != 0){
                buzzSema.release();
            }else if(mark % 15 == 0){
                fizzbuzzSema.release();
            }else{
                printNumber.accept(mark);
                numberSema.release();
            }
        }
        // mark > n后,释放其他信号量使他们可以各自终止线程
        fizzSema.release();
        buzzSema.release();
        fizzbuzzSema.release();
    }
}
 

 

3.Leetcode 1115 交替打印FooBar

题目:我们提供一个类:

class FooBar {
public void foo() {
    for (int i = 0; i < n; i++) {
      print("foo");
  }
}

public void bar() {
    for (int i = 0; i < n; i++) {
      print("bar");
    }
}
}

两个不同的线程将会共用一个 FooBar 实例。其中一个线程将会调用 foo() 方法,另一个线程将会调用 bar() 方法。

请设计修改程序,以确保 "foobar" 被输出 n 次。

方法1:boolean变量+yield

用boolean变量isPrint控制线程先后,这里不用isPrint,执行会超时。

class FooBar {
    private int n;
    private boolean isPrint;
    public FooBar(int n) {
        this.n = n;
    }

    public void foo(Runnable printFoo) throws InterruptedException {
        
        for (int i = 0; i < n; i++) {
        	// printFoo.run() outputs "foo". Do not change or remove this line.
            while(isPrint){
                Thread.yield();
            }
        	printFoo.run();
            isPrint = true;
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        
        for (int i = 0; i < n; i++) {
            while(!isPrint){
                Thread.yield();
            }
            // printBar.run() outputs "bar". Do not change or remove this line.
        	printBar.run();
            isPrint = false;
        }
    }
}

方法2:wait/notify

用布尔型变量isPrint来控制两个线程的调用顺序

class FooBar {
    private int n;
    private boolean isPrint = false;
    public FooBar(int n) {
        this.n = n;
    }

    public void foo(Runnable printFoo) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            synchronized(this){
                while(isPrint){ //超过两个线程要用while,两个线程可以用if代替while
                    this.wait();
                }
                printFoo.run();
                isPrint = true;
                this.notify();
            }
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            synchronized(this){
                while(!isPrint){
                    this.wait();
                }
                printBar.run();
                isPrint = false;
                this.notify();
            }
        }
    }
}

方法3:CountDownLatch

用两个CountDownLatch相互限制

class FooBar {
    private int n;
    private CountDownLatch countFoo;
    private CountDownLatch countBar;

    public FooBar(int n) {
        this.n = n;
        countFoo = new CountDownLatch(0);
        countBar = new CountDownLatch(1);
    }

    public void foo(Runnable printFoo) throws InterruptedException {
        
        for (int i = 0; i < n; i++) {
            
        	// printFoo.run() outputs "foo". Do not change or remove this line.
            countFoo.await();
        	printFoo.run();
            countFoo = new CountDownLatch(1);
            countBar.countDown();
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        
        for (int i = 0; i < n; i++) {
            
            // printBar.run() outputs "bar". Do not change or remove this line.
            countBar.await();
        	printBar.run();
            countBar = new CountDownLatch(1);
            countFoo.countDown();
        }
    }
}

  

 

 

 

方法4:semaphore

两个线程互相控制对方的信号量,当一个线程执行完,释放另一个线程的信号量,并且等待自己的信号量。

 
import java.util.concurrent.Semaphore;

public class FooBar {
    private int n;
    //here is the full path, or maybe cann't compile in leetcode.
    Semaphore semaphoreFoo=new Semaphore(0);
    Semaphore semaphoreBar=new Semaphore(0);

    public FooBar(int n) {
        this.n = n;
    }

    public void foo(Runnable printFoo) throws InterruptedException {

        for (int i = 0; i < n; i++) {
            printFoo.run();
            //由于下面阻塞了,所以这里变为0,下面的方法就能继续执行
            semaphoreBar.release();
            //这里让他等一会,等到bar()执行完
            semaphoreFoo.acquire();
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {

        for (int i = 0; i < n; i++) {
            // 进来先变为1,就会等上面的release()使他变为0,才进行,所以肯定在foo之后。
            semaphoreBar.acquire();
            printBar.run();
            //bar()执行完了,就让foo()继续。
            semaphoreFoo.release();
        }
    }
}
 

 

4.Leetcode 1116 打印零与奇偶数

题目:假设有这么一个类:

class ZeroEvenOdd {
  public ZeroEvenOdd(int n) { ... }  // 构造函数
  public void zero(printNumber) { ... } // 仅打印出 0
  public void even(printNumber) { ... } // 仅打印出 偶数
  public void odd(printNumber) { ... } // 仅打印出 奇数
}

相同的一个 ZeroEvenOdd 类实例将会传递给三个不同的线程:

  1. 线程 A 将调用 zero(),它只输出 0 。
  2. 线程 B 将调用 even(),它只输出偶数。
  3. 线程 C 将调用 odd(),它只输出奇数。

每个线程都有一个 printNumber 方法来输出一个整数。请修改给出的代码以输出整数序列 010203040506... ,其中序列的长度必须为 2n。 

方法1:semaphore

设置3个semaphore

先执行zeroSema输出0,再判断奇偶数;

如果是奇数就执行evenSema,计算1,3,5,...,n;

如果是偶数就执行oddSema,计算2,4,6,...,n;

每次奇偶数进程执行后,释放zeroSema,重新判断奇偶数。

package 多线程.leetcode;

import java.util.concurrent.Semaphore;
import java.util.function.IntConsumer;

/**
 * 功能描述:打印0与奇偶数
 *
 * @author nxf
 * @since 2020-06-10
 */
class ZeroEvenOdd2 {
    private int n;
    private Semaphore zeroSema = new Semaphore(1);
    private Semaphore evenSema = new Semaphore(0);
    private Semaphore oddSema = new Semaphore(0);
    public ZeroEvenOdd2(int n) {
        this.n = n;
    }

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void zero(IntConsumer printNumber) throws InterruptedException {
        for(int i=1; i<=n; i++){
            zeroSema.acquire();
            printNumber.accept(0);
            if(i % 2 == 0){
                evenSema.release();
            } else{
                oddSema.release();
            }
        }
    }

    public void even(IntConsumer printNumber) throws InterruptedException {
        for(int i=2; i <= n; i+=2){
            evenSema.acquire();
            printNumber.accept(i);
            zeroSema.release();
        }
    }

    public void odd(IntConsumer printNumber) throws InterruptedException {
        for(int i=1; i <= n; i+=2){
            oddSema.acquire();
            printNumber.accept(i);
            zeroSema.release();
        }
    }

    public static void main(String[] args) {
        try {
            char integer = (char) System.in.read();
            System.out.println("传入n: " + integer);
            int input = (char) integer - (char) '0';
            ZeroEvenOdd zeroEvenOdd = new ZeroEvenOdd(input);
            IntConsumer intConsumer = value -> System.out.println(value);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        zeroEvenOdd.zero(intConsumer);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        zeroEvenOdd.odd(intConsumer);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        zeroEvenOdd.even(intConsumer);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
     
posted @ 2020-06-09 23:19  nxf_rabbit75  阅读(513)  评论(0编辑  收藏  举报