[LeetCode] 1116. 打印零与奇偶数(Medium)Java语言题解

1.题目相关

①题目及示例

Alt text
Alt text

②相关标签

  • 多线程

③题目地址


2.解题方法

①synchronized 关键字 + this 锁对象

  • 原因:三个线程属于同一个实例,虽然三个线程访问的是一个类的不同的普通同步方法,但是三个普通同步方法默认都是以 this 对象作为同步方法的锁,所以它们会争抢同一把锁(对于同一个实例来讲,三个方法的 this 对象是同一个)。
  • 结果:三个线程争抢同一把锁,同一时刻只能有一个线程执行该线程对应的同步方法,然后再使用 wait 方法和 notify 方法,使得三个线程按题意顺序运行。

②lock + condition

  • lock 代替 synchronized 关键字
  • condition 中的 await 方法和 release 方法代替 Object 对象的 wait 方法和 notify 方法

③信号量

  • 三个线程第一次执行时,因为只有 zero 信号量的计数器初始值为 1,其余都为 0,所以 zero 线程先执行,让它不断判断接下来该哪个线程执行,并释放对应线程的信号量,对应线程执行一次循环后,释放 zero 信号量,以便 zero 线程继续判断。

④volatile 关键字

  • 直接使用 volatile 关键字控制三个线程的执行顺序
  • 具体实现见代码

3.代码详解

①synchronized 关键字 + this 锁对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// 1.synchronized关键字+this锁对象(三个不同的线程将会共用一个ZeroEvenOdd实例)
class ZeroEvenOdd {
private int n;
private volatile int flag = 0;

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

public void zero(IntConsumer printNumber) throws InterruptedException {
for (int i = 1; i <= n; i++) {
synchronized(this) {
while (flag != 0) {
// 自己陷入等待
this.wait();
}
printNumber.accept(0);
if (i % 2 != 0) {
flag = 1;
}
else {
flag = 2;
}
// 唤醒所有线程
this.notifyAll();
}
}
}

public void even(IntConsumer printNumber) throws InterruptedException {
for (int i = 2; i <= n; i += 2) {
synchronized(this) {
while (flag != 2) {
// 自己陷入等待
this.wait();
}
printNumber.accept(i);
flag = 0;
// 唤醒所有线程
this.notifyAll();
}
}
}

public void odd(IntConsumer printNumber) throws InterruptedException {
for (int i = 1; i <= n; i += 2) {
synchronized(this) {
while (flag != 1) {
// 自己陷入等待
this.wait();
}
printNumber.accept(i);
flag = 0;
// 唤醒所有线程
this.notifyAll();
}
}
}
}

②lock + condition

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 2.lock+condition
class ZeroEvenOdd {
private int n;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private volatile int flag = 0;

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

public void zero(IntConsumer printNumber) throws InterruptedException {
for (int i = 1; i <= n; i++) {
lock.lock();
while (flag != 0) {
// 自己陷入等待
condition.await();
}
printNumber.accept(0);
if (i % 2 != 0) {
flag = 1;
}
else {
flag = 2;
}
// 唤醒所有线程
condition.signalAll();
lock.unlock();
}
}

public void even(IntConsumer printNumber) throws InterruptedException {
for (int i = 2; i <= n; i += 2) {
lock.lock();
while (flag != 2) {
// 自己陷入等待
condition.await();
}
printNumber.accept(i);
flag = 0;
// 唤醒所有线程
condition.signalAll();
lock.unlock();
}
}

public void odd(IntConsumer printNumber) throws InterruptedException {
for (int i = 1; i <= n; i += 2) {
lock.lock();
while (flag != 1) {
// 自己陷入等待
condition.await();
}
printNumber.accept(i);
flag = 0;
// 唤醒所有线程
condition.signalAll();
lock.unlock();
}
}
}

③信号量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 3.信号量(Semaphore)
class ZeroEvenOdd {
private int n;
private Semaphore zero = new Semaphore(1);
private Semaphore even = new Semaphore(0);
private Semaphore odd = new Semaphore(0);

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

public void zero(IntConsumer printNumber) throws InterruptedException {
for (int i = 1; i <= n; i++) {
// 获取zero信号量的一个许可
zero.acquire();
printNumber.accept(0);
if (i % 2 != 0) {
// 释放odd信号量的一个许可
odd.release();
}
else {
// 释放even信号量的一个许可
even.release();
}
}
}

public void even(IntConsumer printNumber) throws InterruptedException {
for (int i = 2; i <= n; i += 2) {
// 获取even信号量的一个许可
even.acquire();
printNumber.accept(i);
// 释放zero信号量的一个许可
zero.release();
}
}

public void odd(IntConsumer printNumber) throws InterruptedException {
for (int i = 1; i <= n; i += 2) {
// 获取odd信号量的一个许可
odd.acquire();
printNumber.accept(i);
// 释放zero信号量的一个许可
zero.release();
}
}
}

④volatile 关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 4.volatile
class ZeroEvenOdd {
private int n;
private volatile int flag = 0;

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

public void zero(IntConsumer printNumber) throws InterruptedException {
for (int i = 1; i <= n; i++) {
while (flag != 0) {
Thread.sleep(1);
}
printNumber.accept(0);
if (i % 2 != 0) {
flag = 1;
}
else {
flag = 2;
}
}
}

public void even(IntConsumer printNumber) throws InterruptedException {
for (int i = 2; i <= n; i += 2) {
while (flag != 2) {
Thread.sleep(1);
}
printNumber.accept(i);
flag = 0;
}
}

public void odd(IntConsumer printNumber) throws InterruptedException {
for (int i = 1; i <= n; i += 2) {
while (flag != 1) {
Thread.sleep(1);
}
printNumber.accept(i);
flag = 0;
}
}
}

附录

0%