异步、阻塞、同步阻塞、异步阻塞 完整区分

一、基础定义

1. 阻塞 / 非阻塞(等待方式,同步维度)

针对调用方等待结果的行为:

  • 阻塞:调用后线程卡住,原地等待,不做别的事,拿到结果才往下走;
  • 非阻塞:调用立刻返回,不会卡住线程,需要主动轮询查结果。

2. 同步 / 异步(结果通知方式)

针对谁主动拿结果

  • 同步:调用方主动去获取返回值;
  • 异步:被调用方完成后主动回调通知调用方,不用轮询。

二、四种组合

  1. 同步阻塞(最常见) 普通接口调用、JDBC 查询、synchronized抢锁: 调用后线程卡死等待,返回结果才继续。
java 复制代码
// 同步阻塞
String resp = httpClient.get("xxx"); // 线程卡住,等网络返回
  1. 同步非阻塞 调用马上返回,不断循环轮询结果,线程空转浪费 CPU。

  2. 异步非阻塞(高性能 NIO、Netty、CompletableFuture) 调用立刻释放线程,有结果回调,线程去处理其他任务,高并发首选。

  3. 异步阻塞(重点) 发起异步任务,但主线程主动阻塞等待异步任务完成

  • 任务执行是异步(新开线程 / 线程池跑);
  • 主线程调用 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);
    }
}

执行流程:

  1. 开新线程跑任务(异步执行);
  2. 主线程执行到 future.get() 停止,阻塞等待 2 秒;
  3. 异步线程跑完,主线程解除阻塞继续。

核心:任务异步执行,但主线程主动阻塞等待结果 = 异步阻塞

四、其他异步阻塞场景

  1. ExecutorService 的 Future.get()
java 复制代码
ExecutorService pool = Executors.newFixedThreadPool(2);
Future<Integer> future = pool.submit(() -> {
    Thread.sleep(1000);
    return 100;
});
Integer num = future.get(); // 主线程阻塞
  1. 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);

异步阻塞

任务丢给其他线程异步跑,主线程只是等待结果,任务本身并发执行,吞吐量更高。

  • 同步阻塞:串行;
  • 异步阻塞:并行执行任务,主线程阻塞等汇总。

六、使用场景

  1. 多任务并行查询,需要等全部结果再统一返回前端
java 复制代码
// 并行查商品、库存、价格,全部查完再组装数据
CompletableFuture<Goods> goodsFuture = CompletableFuture.supplyAsync(this::getGoods);
CompletableFuture<Stock> stockFuture = CompletableFuture.supplyAsync(this::getStock);
// 阻塞等待两个异步任务完成
Goods goods = goodsFuture.get();
Stock stock = stockFuture.get();
  1. 批量多线程处理数据,主线程等待全部处理完毕再提交事务;
  2. 多 RPC 并行调用,汇总结果后统一响应。

七、坑点

  1. future.get() 无限阻塞,建议带超时时间 get(3, TimeUnit.SECONDS) 防止卡死;
  2. 异步阻塞会占用主线程,如果是 Web 接口,会占用 Tomcat 业务线程,并发高容易耗尽线程池;
  3. 如果不需要同步等待,改用回调 whenComplete(),变成异步非阻塞,性能更好:
java 复制代码
future.whenComplete((res, ex) -> {
    // 回调,不阻塞主线程
});

极简总结

  • 异步:任务交给别的线程并发执行;
  • 阻塞:当前线程停下等结果;
  • 异步阻塞:任务异步跑,但主线程调用 get/await 卡住等待异步任务完成。