1116. 打印零与奇偶数

目录

1、题目描述

2、解法

2.1、解法一(等待法)

2.2、解法二(信号量法)

3、总结


1、题目描述

 

2、解法

2.1、解法一(等待法)

定义一个状态flag,根据flag的取值不同依次让zero()、even()和odd()的线程处于等待状态或者结束等待以达到交替打印零奇偶。

package 打印零与奇偶数_1116;

import java.util.function.IntConsumer;

/**
 * @description: 等待法
 * @author: wu linchun
 * @time: 2021/3/27 20:22
 */

public class ZeroEvenOdd {
    private int n;
    private int odd = 1;
    private int even = 2;
    private String flag = "zero";   //作为锁🔒

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

    // printNumber.accept(x) outputs "x", where x is an integer.
    public synchronized void zero(IntConsumer printNumber) throws InterruptedException {
        while (odd<n||even<n) {    //当最大值<n的时候才会有打印0的情况
            if (!flag.equals("zero")) {  //如果当前状态不是打印0,则zero()这个方法线程处于等待状态
                this.wait();
            }
            this.notifyAll();  //唤醒所有处于等待中的线程
            if (flag.equals("zero")) {  //确定下一个要处于等待中的线程
                if (even<odd) {
                    flag = "even";
                }else {
                    flag = "odd";
                }
                printNumber.accept(0);
            }
        }
    }

    public synchronized void even(IntConsumer printNumber) throws InterruptedException {
        while (even<=n) {
            if (!flag.equals("even")) {
                this.wait();
            }
            this.notifyAll();
            if (flag.equals("even")) {  //打印完偶数后,下一个就是打印0
                flag = "zero";
                printNumber.accept(even);
                even+=2;
            }
        }
    }

    public synchronized void odd(IntConsumer printNumber) throws InterruptedException {
        while (odd<=n) {
            if (!flag.equals("odd")) {
                this.wait();
            }
            this.notifyAll();
            if (flag.equals("odd")) {  //打印完奇数后,下一个就是打印0
                flag = "zero";
                printNumber.accept(odd);
                odd+=2;
            }
        }
    }

    public static void main(String[] args) {
        //long startTime=System.nanoTime();   //获取开始时间
        long  startTime = System.currentTimeMillis();
        ZeroEvenOdd zeroEvenOdd = new ZeroEvenOdd(5);
        new Thread(() -> {
            try {
                zeroEvenOdd.zero((x) -> {
                    System.out.print(x);
                });
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "A").start();
        new Thread(() -> {
            try {
                zeroEvenOdd.even((x) -> {
                    System.out.print(x);
                });
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "B").start();
        new Thread(() -> {
            try {
                zeroEvenOdd.odd((x) -> {
                    System.out.print(x);
                });
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "C").start();
        //long endTime=System.nanoTime(); //获取结束时间
        long endTime = System.currentTimeMillis();    //获取结束时间
        //System.out.println("程序运行时间: "+(endTime-startTime)+"ns");
        System.out.println("程序运行时间: "+(endTime-startTime)+"ms");
    }
}

不过该方法由于时间复杂度和空间复杂度的问题没法在leetcode上提交通过。

 

2.2、解法二(信号量法)

这种方法是参考别人的,就是采用信号量控制这三个方法依次执行。

package 打印零与奇偶数_1116;

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

/**
 * @description:
 * @author: wu linchun
 * @time: 2021/3/27 22:28
 */

public class ZeroEvenOdd1 {
    private int n;
    private Semaphore zero = new Semaphore(1);
    private Semaphore even = new Semaphore(0);
    private Semaphore odd = new Semaphore(0);

    public ZeroEvenOdd1(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++) {
            zero.acquire();
            printNumber.accept(0);
            if (i % 2 == 1) {
                odd.release();
            } else {
                even.release();
            }
        }
    }

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

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

    public static void main(String[] args) {
        long  startTime = System.currentTimeMillis();
        ZeroEvenOdd zeroEvenOdd = new ZeroEvenOdd(5);
        new Thread(() -> {
            try {
                zeroEvenOdd.zero(System.out::print);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(() -> {
            try {
                zeroEvenOdd.even(System.out::print);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(() -> {
            try {
                zeroEvenOdd.odd(System.out::print);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        long endTime = System.currentTimeMillis();    //获取结束时间
        //System.out.println("程序运行时间: "+(endTime-startTime)+"ns");
        System.out.println("程序运行时间: "+(endTime-startTime)+"ms");
    }
}

 

3、总结

我个人认为等待法之所以会有较高的时间和空间复杂度是因为wait()只是让线程主动进入等待状态,等待状态的线程还是处于线程同步中,虽然不占用CPU工作,但线程同步也会占用一定的时间和空间资源。而信号量法中,在线程没有分配到信号量时是处于阻塞状态,阻塞状态的线程不处于线程同步中,因此虽然同样不占用CPU工作,但相较于始终处于同步状态的wait线程来说,对系统的时间和空间开销要小一些。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

金斗潼关

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值