多线程交替打印数字是 Java 面试中的经典题目。本文将深入解析如何利用 synchronized 配合 wait/notify,以及 ReentrantLock 配合 Condition 等方案方案实现 1-50 的按序打印。
在Java中,让两个线程交替打印奇偶数,核心在于实现线程间的精确协作,即线程之间的通信。
使用 synchronized、wait 和 notify
这是最经典的线程通信方式,易于理解,适合掌握基础同步机制。
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
| public class OddEvenPrinter {
private static final int MAX = 50;
private static int counter = 1; // 共享计数器
private static final Object lock = new Object(); // 共享锁对象
public static void main(String[] args) {
// 打印奇数的线程
Thread oddThread = new Thread(() -> {
while (counter <= MAX) {
synchronized (lock) {
// 如果counter是偶数,则奇数线程等待
while (counter % 2 == 0) {
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
// 打印并增加计数器
System.out.println("奇数线程: " + counter);
counter++;
// 通知等待的线程(偶数线程)
lock.notify();
}
}
});
// 打印偶数的线程
Thread evenThread = new Thread(() -> {
while (counter <= MAX) {
synchronized (lock) {
// 如果counter是奇数,则偶数线程等待
while (counter % 2 == 1) {
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
System.out.println("偶数线程: " + counter);
counter++;
lock.notify();
}
}
});
oddThread.start();
evenThread.start();
}
}
|
使用 ReentrantLock 和 Condition
ReentrantLock搭配 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
| import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class OddEvenPrinterWithLock {
private static final int MAX = 50;
private static int counter = 1;
private static final ReentrantLock lock = new ReentrantLock();
private static final Condition oddCondition = lock.newCondition(); // 奇数线程的条件队列
private static final Condition evenCondition = lock.newCondition(); // 偶数线程的条件队列
public static void main(String[] args) {
Thread oddThread = new Thread(() -> {
while (counter <= MAX) {
lock.lock();
try {
while (counter % 2 == 0) {
oddCondition.await(); // 奇数线程等待
}
if (counter <= MAX) {
System.out.println("奇数线程: " + counter);
counter++;
}
evenCondition.signal(); // 唤醒偶数线程
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
} finally {
lock.unlock();
}
}
});
Thread evenThread = new Thread(() -> {
while (counter <= MAX) {
lock.lock();
try {
while (counter % 2 == 1) {
evenCondition.await(); // 偶数线程等待
}
if (counter <= MAX) {
System.out.println("偶数线程: " + counter);
counter++;
}
oddCondition.signal(); // 唤醒奇数线程
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
} finally {
lock.unlock();
}
}
});
oddThread.start();
evenThread.start();
}
}
|
使用 Semaphore(信号量)
信号量通过许可证的分配来控制执行顺序,逻辑清晰。
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
| import java.util.concurrent.Semaphore;
public class OddEvenPrinterWithSemaphore {
private static final int MAX = 50;
private static int counter = 1;
private static final Semaphore oddSemaphore = new Semaphore(1); // 奇数信号量,初始有许可
private static final Semaphore evenSemaphore = new Semaphore(0); // 偶数信号量,初始无许可
public static void main(String[] args) {
Thread oddThread = new Thread(() -> {
while (counter <= MAX) {
try {
oddSemaphore.acquire(); // 获取奇数信号量许可
if (counter <= MAX) {
System.out.println("奇数线程: " + counter);
counter++;
}
evenSemaphore.release(); // 释放偶数信号量许可
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
});
Thread evenThread = new Thread(() -> {
while (counter <= MAX) {
try {
evenSemaphore.acquire(); // 获取偶数信号量许可
if (counter <= MAX) {
System.out.println("偶数线程: " + counter);
counter++;
}
oddSemaphore.release(); // 释放奇数信号量许可
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
});
oddThread.start();
evenThread.start();
}
}
|