Skip to content

Add CyclicThreadPoolDeadLockDemo #91

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ Examples of concurrency problems you encountered in development are welcome to p
- [Demo description](#demo-description-8)
- [Problem statement](#problem-statement-8)
- [Quickly run](#quickly-run-8)
- [🍺 Cyclic Thread Pool Deadlock](#-cyclic-thread-pool-deadlock)
- [Demo Description](#demo-description-9)
- [Problem Description](#problem-description-9)
- [Quick Run](#quick-run-9)

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

Expand Down Expand Up @@ -257,3 +261,24 @@ and requires specific hardware and JVM environments).
```bash
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.FinalInitialDemo
```

## 🍺 Cyclic Thread Pool Deadlock

Demo class [`CyclicThreadPoolDeadLockDemo`](../../src/main/java/fucking/concurrency/demo/CyclicThreadPoolDeadLockDemo.java).

### Demo Description

This example demonstrates the issue of deadlock caused by cyclic dependencies between tasks when using thread pools,
and how to avoid this situation using `CompletableFuture`.

### Problem Description

In the `badCase`, two thread pools, `pool1` and `pool2`, submit tasks to each other, forming a cyclic dependency.
When the thread pool's threads are exhausted, all executing tasks wait for other tasks to complete, leading to a deadlock.
The `goodCase` resolves the deadlock issue by using asynchronous chained calls with `CompletableFuture`, thus avoiding thread pool blocking.

### Quick Run

```bash
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.CyclicThreadPoolDeadLockDemo
```
20 changes: 20 additions & 0 deletions docs/zh-CN/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@
- [Demo说明](#demo%E8%AF%B4%E6%98%8E-7)
- [问题说明](#%E9%97%AE%E9%A2%98%E8%AF%B4%E6%98%8E-7)
- [快速运行](#%E5%BF%AB%E9%80%9F%E8%BF%90%E8%A1%8C-7)
- [🍺 线程池循环引用死锁](#-线程池循环引用死锁)
- [Demo说明](#demo%E8%AF%B4%E6%98%8E-8)
- [问题说明](#%E9%97%AE%E9%A2%98%E8%AF%B4%E6%98%8E-8)
- [快速运行](#%E5%BF%AB%E9%80%9F%E8%BF%90%E8%A1%8C-8)
- [一些并发的问题讨论和资料](#%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)

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

## 🍺 线程池循环引用死锁
Demo类[`CyclicThreadPoolDeadLockDemo`](../../src/main/java/fucking/concurrency/demo/CyclicThreadPoolDeadLockDemo.java)。

### Demo说明
该示例展示了在使用线程池时,由于任务间的循环依赖线程池导致死锁的问题,以及如何通过CompletableFuture来避免这种情况。

### 问题说明
在badCase中,两个线程池pool1和pool2相互提交任务,形成循环依赖。当线程池的线程数耗尽时,所有执行中的任务都在等待其他任务完成,导致死锁。
goodCase通过使用CompletableFuture的异步链式调用,避免了线程池的阻塞,从而解决了死锁问题。

### 快速运行

```bash
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.CyclicThreadPoolDeadLockDemo
```

## 一些并发的问题讨论和资料

- [ibm developerworks - 多核系统上的`Java`并发缺陷模式(`bug patterns`)](http://www.ibm.com/developerworks/cn/java/j-concurrencybugpatterns/)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package fucking.concurrency.demo;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static java.util.concurrent.CompletableFuture.supplyAsync;
/**
* @author Eric Lin (linqinghua4 at gmail dot com)
*/
public class CyclicThreadPoolDeadLockDemo {
public static void main(String[] args) throws InterruptedException {
if (args.length > 0 && "good".equals(args[0])) {
goodCase();
} else {
badCase();
}
}

static void badCase() throws InterruptedException {
int poolSize = 16;
ThreadPoolExecutor pool1 = new ThreadPoolExecutor(poolSize, poolSize,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>());
ThreadPoolExecutor pool2 = new ThreadPoolExecutor(poolSize, poolSize,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>());
List<Future<Integer>> futures = new ArrayList<>();
for (int i = 0; i < 100; i++) {
int finalI = i;
Future<Integer> future = pool1.submit(() -> {
System.out.println("step1, i = " + finalI);
return 1 + getUnchecked(pool2.submit(() -> {
System.out.println("step2, i = " + finalI);
return 2 + getUnchecked(pool1.submit(() -> {
System.out.println("step3, i = " + finalI);
return 3;
}));
}));
});
futures.add(future);
}
// 无法计算,死锁
int result = futures.stream()
.mapToInt(CyclicThreadPoolDeadLockDemo::getUnchecked)
.sum();
System.out.println("result = " + result);
// 无法关闭
pool1.awaitTermination(20, TimeUnit.SECONDS);
pool2.awaitTermination(20, TimeUnit.SECONDS);
}

static void goodCase() throws InterruptedException {
int poolSize = 16;
ThreadPoolExecutor pool1 = new ThreadPoolExecutor(poolSize, poolSize,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>());
ThreadPoolExecutor pool2 = new ThreadPoolExecutor(poolSize, poolSize,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>());
List<Future<Integer>> futures = new ArrayList<>();
for (int i = 0; i < 100; i++) {
int finalI = i;
CompletableFuture<Integer> cf1 = supplyAsync(() -> {
System.out.println("step1, i = " + finalI);
return 1;
}, pool1);
CompletableFuture<Integer> cf2 = supplyAsync(() -> {
System.out.println("step2, i = " + finalI);
return 2;
}, pool2);
CompletableFuture<Integer> cf3 = supplyAsync(() -> {
System.out.println("step3, i = " + finalI);
return 3;
}, pool1);
Future<Integer> future =
cf1.thenComposeAsync(x ->
cf2.thenComposeAsync(y ->
cf3.thenApply(z ->
x + y + z), pool2), pool1);
futures.add(future);
}
System.out.println("size1 = " + pool1.getQueue().size());
System.out.println("size2 = " + pool2.getQueue().size());
int result = futures.stream()
.mapToInt(CyclicThreadPoolDeadLockDemo::getUnchecked)
.sum();
System.out.println("result = " + result);
pool1.awaitTermination(20, TimeUnit.SECONDS);
pool2.awaitTermination(20, TimeUnit.SECONDS);
}

static <T> T getUnchecked(Future<T> future) {
try {
return future.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}