面试:如果线程B需要等待线程A处理完,线程C需要等待线程B处理完,你会怎么做?

如何在 Java 中实现线程间的有序执行?本文深度解析“线程 B 等待线程 A,线程 C 等待线程 B”的经典面试题。

在需要线程按A→B→C顺序执行的场景下,Java提供了多种实现方式。

使用CountDownLatch

这种方式逻辑清晰,是解决此类问题的标准模式。

 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
import java.util.concurrent.CountDownLatch;

public class ThreadOrderWithLatch {
    public static void main(String[] args) {
        // 创建两个门闩,分别控制 A->B 和 B->C
        CountDownLatch latchAB = new CountDownLatch(1); // 初始计数为1
        CountDownLatch latchBC = new CountDownLatch(1); // 初始计数为1

        Thread threadA = new Thread(() -> {
            System.out.println("线程A开始工作...");
            // 模拟A的处理逻辑
            try { 
                Thread.sleep(1000); 
            } catch (InterruptedException e) { 
                e.printStackTrace(); 
            }
            System.out.println("线程A处理完毕!");
            latchAB.countDown(); // A完成后,计数器减1,通知B可以开始了
        });

        Thread threadB = new Thread(() -> {
            try {
                latchAB.await(); // B在此等待,直到A完成(计数器为0)
                System.out.println("线程B开始工作...");
                // 模拟B的处理逻辑
                try { 
                    Thread.sleep(1000); 
                } catch (InterruptedException e) { 
                    e.printStackTrace(); 
                }
                System.out.println("线程B处理完毕!");
                latchBC.countDown(); // B完成后,计数器减1,通知C可以开始了
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread threadC = new Thread(() -> {
            try {
                latchBC.await(); // C在此等待,直到B完成(计数器为0)
                System.out.println("线程C开始工作...");
                // 模拟C的处理逻辑
                try { 
                    Thread.sleep(1000); 
                } catch (InterruptedException e) { 
                    e.printStackTrace(); 
                }
                System.out.println("线程C处理完毕!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // 启动线程(启动顺序可以任意,执行顺序由门闩控制)
        threadA.start();
        threadB.start();
        threadC.start();
    }
}

使用单线程线程池 (ExecutorService)

这是实现顺序执行最高效、最简洁的方式

 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
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadOrderWithSingleThreadPool {
    public static void main(String[] args) {
        // 创建一个单线程的线程池
        ExecutorService executor = Executors.newSingleThreadExecutor();

        // 按顺序提交任务
        executor.submit(() -> {
            System.out.println("线程A开始工作...");
            try { 
                Thread.sleep(1000); 
            } catch (InterruptedException e) { 
                e.printStackTrace(); 
            }
            System.out.println("线程A处理完毕!");
        });

        executor.submit(() -> {
            System.out.println("线程B开始工作...");
            try { 
                Thread.sleep(1000); 
            } catch (InterruptedException e) { 
                e.printStackTrace(); 
            }
            System.out.println("线程B处理完毕!");
        });

        executor.submit(() -> {
            System.out.println("线程C开始工作...");
            try { 
                Thread.sleep(1000); 
            } catch (InterruptedException e) { 
                e.printStackTrace(); 
            }
            System.out.println("线程C处理完毕!");
        });

        // 关闭线程池(不再接受新任务,已提交的任务会顺序执行完)
        executor.shutdown();
    }
}

使用 Thread.join()

这是最直接、最符合直觉的方式。在后续线程中调用前驱线程的 join()方法,使其等待前驱线程执行完毕。

 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
public class ThreadOrderWithJoin {
    public static void main(String[] args) throws InterruptedException {
        // 定义任务
        Runnable taskA = () -> {
            System.out.println("线程A开始工作...");
            try { 
                Thread.sleep(1000); 
            } catch (InterruptedException e) { 
                e.printStackTrace(); 
            }
            System.out.println("线程A处理完毕!");
        };

        Runnable taskB = () -> {
            System.out.println("线程B开始工作...");
            try { 
                Thread.sleep(1000); 
            } catch (InterruptedException e) { 
                e.printStackTrace(); 
            }
            System.out.println("线程B处理完毕!");
        };

        Runnable taskC = () -> {
            System.out.println("线程C开始工作...");
            try { 
                Thread.sleep(1000); 
            } catch (InterruptedException e) { 
                e.printStackTrace(); 
            }
            System.out.println("线程C处理完毕!");
        };

        // 创建线程
        Thread threadA = new Thread(taskA);
        Thread threadB = new Thread(taskB);
        Thread threadC = new Thread(taskC);

        // 启动并等待
        System.out.println("启动线程A...");
        threadA.start();
        threadA.join(); // 主线程(此处可看作调度者)等待A结束

        System.out.println("启动线程B...");
        threadB.start();
        threadB.join(); // 等待B结束

        System.out.println("启动线程C...");
        threadC.start();
        threadC.join(); // 等待C结束

        System.out.println("所有任务按顺序完成。");
    }
}

使用 Semaphore(信号量)

利用信号量“许可”的获取和释放,可以非常灵活地控制线程的执行顺序。我们创建两个初始许可为0的信号量,形成一条“许可传递链”。

 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
import java.util.concurrent.Semaphore;

public class ThreadOrderWithSemaphore {
    public static void main(String[] args) {
        // 创建两个信号量,初始许可数均为0
        Semaphore semaphoreAB = new Semaphore(0); // 控制B能否开始(由A释放)
        Semaphore semaphoreBC = new Semaphore(0); // 控制C能否开始(由B释放)

        Thread threadA = new Thread(() -> {
            System.out.println("线程A开始工作...");
            try { 
                Thread.sleep(1000); 
            } catch (InterruptedException e) { 
                e.printStackTrace(); 
            }
            System.out.println("线程A处理完毕!");
            semaphoreAB.release(); // A完成后,释放一个许可给B(允许B开始)
        });

        Thread threadB = new Thread(() -> {
            try {
                semaphoreAB.acquire(); // B尝试获取许可,没有则阻塞等待(直到A释放)
                System.out.println("线程B开始工作...");
                Thread.sleep(1000);
                System.out.println("线程B处理完毕!");
                semaphoreBC.release(); // B完成后,释放一个许可给C(允许C开始)
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread threadC = new Thread(() -> {
            try {
                semaphoreBC.acquire(); // C尝试获取许可,没有则阻塞等待(直到B释放)
                System.out.println("线程C开始工作...");
                Thread.sleep(1000);
                System.out.println("线程C处理完毕!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // 启动所有线程(启动顺序任意,执行顺序由信号量控制)
        threadA.start();
        threadB.start();
        threadC.start();
    }
}
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计