如何在 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();
}
}
|