一、基础定义
1. 阻塞 / 非阻塞(等待方式,同步维度)
针对调用方等待结果的行为:
- 阻塞:调用后线程卡住,原地等待,不做别的事,拿到结果才往下走;
- 非阻塞:调用立刻返回,不会卡住线程,需要主动轮询查结果。
2. 同步 / 异步(结果通知方式)
针对谁主动拿结果:
- 同步:调用方主动去获取返回值;
- 异步:被调用方完成后主动回调通知调用方,不用轮询。
二、四种组合
- 同步阻塞(最常见) 普通接口调用、JDBC 查询、
synchronized抢锁: 调用后线程卡死等待,返回结果才继续。
java
// 同步阻塞
String resp = httpClient.get("xxx"); // 线程卡住,等网络返回
-
同步非阻塞 调用马上返回,不断循环轮询结果,线程空转浪费 CPU。
-
异步非阻塞(高性能 NIO、Netty、CompletableFuture) 调用立刻释放线程,有结果回调,线程去处理其他任务,高并发首选。
-
异步阻塞(重点) 发起异步任务,但主线程主动阻塞等待异步任务完成。
- 任务执行是异步(新开线程 / 线程池跑);
- 主线程调用
get()/join()原地阻塞,等异步结果。
三、异步阻塞代码示例(Java CompletableFuture)
java
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class AsyncBlockDemo {
public static void main(String[] args) throws Exception {
System.out.println("主线程开始");
// 1. 异步提交任务,交给线程池执行(异步执行)
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "异步任务执行完成";
});
System.out.println("异步任务已提交,主线程走到这里");
// 2. 阻塞点:主线程卡住,等待异步任务返回 ------ 【异步阻塞】
String result = future.get();
System.out.println("拿到结果:" + result);
}
}
执行流程:
- 开新线程跑任务(异步执行);
- 主线程执行到
future.get()停止,阻塞等待 2 秒; - 异步线程跑完,主线程解除阻塞继续。
核心:任务异步执行,但主线程主动阻塞等待结果 = 异步阻塞
四、其他异步阻塞场景
- ExecutorService 的
Future.get()
java
ExecutorService pool = Executors.newFixedThreadPool(2);
Future<Integer> future = pool.submit(() -> {
Thread.sleep(1000);
return 100;
});
Integer num = future.get(); // 主线程阻塞
- CountDownLatch.await () 异步多线程执行,主线程阻塞等待全部完成:
java
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try {Thread.sleep(1000);} catch (Exception e){}
latch.countDown();
}).start();
}
latch.await(); // 主线程阻塞,等所有异步线程结束
五、异步阻塞 vs 同步阻塞 区别
同步阻塞
只有一条线程串行执行,等待期间线程完全闲置,无法处理其他任务。
java
// 同步阻塞,单线程等待
Thread.sleep(2000);
异步阻塞
任务丢给其他线程异步跑,主线程只是等待结果,任务本身并发执行,吞吐量更高。
- 同步阻塞:串行;
- 异步阻塞:并行执行任务,主线程阻塞等汇总。
六、使用场景
- 多任务并行查询,需要等全部结果再统一返回前端
java
// 并行查商品、库存、价格,全部查完再组装数据
CompletableFuture<Goods> goodsFuture = CompletableFuture.supplyAsync(this::getGoods);
CompletableFuture<Stock> stockFuture = CompletableFuture.supplyAsync(this::getStock);
// 阻塞等待两个异步任务完成
Goods goods = goodsFuture.get();
Stock stock = stockFuture.get();
- 批量多线程处理数据,主线程等待全部处理完毕再提交事务;
- 多 RPC 并行调用,汇总结果后统一响应。
七、坑点
future.get()无限阻塞,建议带超时时间get(3, TimeUnit.SECONDS)防止卡死;- 异步阻塞会占用主线程,如果是 Web 接口,会占用 Tomcat 业务线程,并发高容易耗尽线程池;
- 如果不需要同步等待,改用回调
whenComplete(),变成异步非阻塞,性能更好:
java
future.whenComplete((res, ex) -> {
// 回调,不阻塞主线程
});
极简总结
- 异步:任务交给别的线程并发执行;
- 阻塞:当前线程停下等结果;
- 异步阻塞:任务异步跑,但主线程调用 get/await 卡住等待异步任务完成。