• 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();
            }
        }
    }
         
  • 相关阅读:
    vue教程2-06 过滤器
    vue教程2-05 v-for循环 重复数据无法添加问题 加track-by='索引'
    vue教程2-04 vue实例简单方法
    Linux文件I/O
    Linux内存管理
    进程中内存地址空间的划分
    Ubuntu12.04 15.04禁止移动介质自动播放
    条件编译,头文件,静态库,共享库与多文件编程
    C语言的函数
    C语言流程控制
  • 原文地址:https://www.cnblogs.com/nxf-rabbit75/p/13080305.html
Copyright © 2020-2023  润新知