面试:创建两个线程,要求按顺序交替打印1-50的数字

多线程交替打印数字是 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();
    }
}
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计