目录
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线程来说,对系统的时间和空间开销要小一些。