Skip to content

Commit 4b2fd6d

Browse files
linzee1linqh
authored andcommitted
Add CyclicThreadPoolDeadLockDemo (#91)
Co-authored-by: linqh <[email protected]>
1 parent 502c228 commit 4b2fd6d

File tree

3 files changed

+149
-0
lines changed

3 files changed

+149
-0
lines changed

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ Examples of concurrency problems you encountered in development are welcome to p
6969
- [Demo description](#demo-description-8)
7070
- [Problem statement](#problem-statement-8)
7171
- [Quickly run](#quickly-run-8)
72+
- [🍺 Cyclic Thread Pool Deadlock](#-cyclic-thread-pool-deadlock)
73+
- [Demo Description](#demo-description-9)
74+
- [Problem Description](#problem-description-9)
75+
- [Quick Run](#quick-run-9)
7276

7377
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
7478

@@ -257,3 +261,24 @@ and requires specific hardware and JVM environments).
257261
```bash
258262
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.FinalInitialDemo
259263
```
264+
265+
## 🍺 Cyclic Thread Pool Deadlock
266+
267+
Demo class [`CyclicThreadPoolDeadLockDemo`](../../src/main/java/fucking/concurrency/demo/CyclicThreadPoolDeadLockDemo.java).
268+
269+
### Demo Description
270+
271+
This example demonstrates the issue of deadlock caused by cyclic dependencies between tasks when using thread pools,
272+
and how to avoid this situation using `CompletableFuture`.
273+
274+
### Problem Description
275+
276+
In the `badCase`, two thread pools, `pool1` and `pool2`, submit tasks to each other, forming a cyclic dependency.
277+
When the thread pool's threads are exhausted, all executing tasks wait for other tasks to complete, leading to a deadlock.
278+
The `goodCase` resolves the deadlock issue by using asynchronous chained calls with `CompletableFuture`, thus avoiding thread pool blocking.
279+
280+
### Quick Run
281+
282+
```bash
283+
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.CyclicThreadPoolDeadLockDemo
284+
```

docs/zh-CN/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@
6868
- [Demo说明](#demo%E8%AF%B4%E6%98%8E-7)
6969
- [问题说明](#%E9%97%AE%E9%A2%98%E8%AF%B4%E6%98%8E-7)
7070
- [快速运行](#%E5%BF%AB%E9%80%9F%E8%BF%90%E8%A1%8C-7)
71+
- [🍺 线程池循环引用死锁](#-线程池循环引用死锁)
72+
- [Demo说明](#demo%E8%AF%B4%E6%98%8E-8)
73+
- [问题说明](#%E9%97%AE%E9%A2%98%E8%AF%B4%E6%98%8E-8)
74+
- [快速运行](#%E5%BF%AB%E9%80%9F%E8%BF%90%E8%A1%8C-8)
7175
- [一些并发的问题讨论和资料](#%E4%B8%80%E4%BA%9B%E5%B9%B6%E5%8F%91%E7%9A%84%E9%97%AE%E9%A2%98%E8%AE%A8%E8%AE%BA%E5%92%8C%E8%B5%84%E6%96%99)
7276

7377
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -232,6 +236,22 @@ writer线程调用类的构造函数,reader线程获取类的非final的成员
232236
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.FinalInitialDemo
233237
```
234238

239+
## 🍺 线程池循环引用死锁
240+
Demo类[`CyclicThreadPoolDeadLockDemo`](../../src/main/java/fucking/concurrency/demo/CyclicThreadPoolDeadLockDemo.java)
241+
242+
### Demo说明
243+
该示例展示了在使用线程池时,由于任务间的循环依赖线程池导致死锁的问题,以及如何通过CompletableFuture来避免这种情况。
244+
245+
### 问题说明
246+
在badCase中,两个线程池pool1和pool2相互提交任务,形成循环依赖。当线程池的线程数耗尽时,所有执行中的任务都在等待其他任务完成,导致死锁。
247+
goodCase通过使用CompletableFuture的异步链式调用,避免了线程池的阻塞,从而解决了死锁问题。
248+
249+
### 快速运行
250+
251+
```bash
252+
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.CyclicThreadPoolDeadLockDemo
253+
```
254+
235255
## 一些并发的问题讨论和资料
236256

237257
- [ibm developerworks - 多核系统上的`Java`并发缺陷模式(`bug patterns`](http://www.ibm.com/developerworks/cn/java/j-concurrencybugpatterns/)
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package fucking.concurrency.demo;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.concurrent.CompletableFuture;
6+
import java.util.concurrent.Future;
7+
import java.util.concurrent.LinkedBlockingQueue;
8+
import java.util.concurrent.ThreadPoolExecutor;
9+
import java.util.concurrent.TimeUnit;
10+
11+
import static java.util.concurrent.CompletableFuture.supplyAsync;
12+
/**
13+
* @author Eric Lin (linqinghua4 at gmail dot com)
14+
*/
15+
public class CyclicThreadPoolDeadLockDemo {
16+
public static void main(String[] args) throws InterruptedException {
17+
if (args.length > 0 && "good".equals(args[0])) {
18+
goodCase();
19+
} else {
20+
badCase();
21+
}
22+
}
23+
24+
static void badCase() throws InterruptedException {
25+
int poolSize = 16;
26+
ThreadPoolExecutor pool1 = new ThreadPoolExecutor(poolSize, poolSize,
27+
0L, TimeUnit.MILLISECONDS,
28+
new LinkedBlockingQueue<>());
29+
ThreadPoolExecutor pool2 = new ThreadPoolExecutor(poolSize, poolSize,
30+
0L, TimeUnit.MILLISECONDS,
31+
new LinkedBlockingQueue<>());
32+
List<Future<Integer>> futures = new ArrayList<>();
33+
for (int i = 0; i < 100; i++) {
34+
int finalI = i;
35+
Future<Integer> future = pool1.submit(() -> {
36+
System.out.println("step1, i = " + finalI);
37+
return 1 + getUnchecked(pool2.submit(() -> {
38+
System.out.println("step2, i = " + finalI);
39+
return 2 + getUnchecked(pool1.submit(() -> {
40+
System.out.println("step3, i = " + finalI);
41+
return 3;
42+
}));
43+
}));
44+
});
45+
futures.add(future);
46+
}
47+
// 无法计算,死锁
48+
int result = futures.stream()
49+
.mapToInt(CyclicThreadPoolDeadLockDemo::getUnchecked)
50+
.sum();
51+
System.out.println("result = " + result);
52+
// 无法关闭
53+
pool1.awaitTermination(20, TimeUnit.SECONDS);
54+
pool2.awaitTermination(20, TimeUnit.SECONDS);
55+
}
56+
57+
static void goodCase() throws InterruptedException {
58+
int poolSize = 16;
59+
ThreadPoolExecutor pool1 = new ThreadPoolExecutor(poolSize, poolSize,
60+
0L, TimeUnit.MILLISECONDS,
61+
new LinkedBlockingQueue<>());
62+
ThreadPoolExecutor pool2 = new ThreadPoolExecutor(poolSize, poolSize,
63+
0L, TimeUnit.MILLISECONDS,
64+
new LinkedBlockingQueue<>());
65+
List<Future<Integer>> futures = new ArrayList<>();
66+
for (int i = 0; i < 100; i++) {
67+
int finalI = i;
68+
CompletableFuture<Integer> cf1 = supplyAsync(() -> {
69+
System.out.println("step1, i = " + finalI);
70+
return 1;
71+
}, pool1);
72+
CompletableFuture<Integer> cf2 = supplyAsync(() -> {
73+
System.out.println("step2, i = " + finalI);
74+
return 2;
75+
}, pool2);
76+
CompletableFuture<Integer> cf3 = supplyAsync(() -> {
77+
System.out.println("step3, i = " + finalI);
78+
return 3;
79+
}, pool1);
80+
Future<Integer> future =
81+
cf1.thenComposeAsync(x ->
82+
cf2.thenComposeAsync(y ->
83+
cf3.thenApply(z ->
84+
x + y + z), pool2), pool1);
85+
futures.add(future);
86+
}
87+
System.out.println("size1 = " + pool1.getQueue().size());
88+
System.out.println("size2 = " + pool2.getQueue().size());
89+
int result = futures.stream()
90+
.mapToInt(CyclicThreadPoolDeadLockDemo::getUnchecked)
91+
.sum();
92+
System.out.println("result = " + result);
93+
pool1.awaitTermination(20, TimeUnit.SECONDS);
94+
pool2.awaitTermination(20, TimeUnit.SECONDS);
95+
}
96+
97+
static <T> T getUnchecked(Future<T> future) {
98+
try {
99+
return future.get();
100+
} catch (Exception e) {
101+
throw new RuntimeException(e);
102+
}
103+
}
104+
}

0 commit comments

Comments
 (0)