Java异步编程的方式

方式 说明 适用场景 核心机制/工具
1 多线程(Thread / Runnable 直接创建线程或使用 Runnable/Callable 实现异步任务 简单异步、底层控制 Thread, Runnable, ExecutorService
2 Future 和 Callable 提交任务并异步获取结果(阻塞式获取) 需要返回值的异步任务 ExecutorService.submit(Callable),返回 Future<T>
3 CompletableFuture (Java 8+) 强大的异步编程工具,支持链式调用、组合、回调、异常处理等 复杂异步流程编排 CompletableFuture.supplyAsync() / thenApply() / thenCompose()
4 回调(Callback) 异步完成后通过回调函数通知 事件驱动、异步IO等 自定义回调接口,常见于旧代码或某些框架
5 响应式编程(Reactive Programming) 基于数据流和变化传播的异步非阻塞模型 高并发、流式处理、前后端交互 Project Reactor(如 Mono/Flux)、RxJava
6 Java 异步 HTTP 客户端(如 HttpClient) 异步发送 HTTP 请求并处理响应 异步网络请求 HttpClient.sendAsync()(Java 11+)

1. 使用ThreadRunnableExecutorService

  • Runnable 定义 "要做什么"(异步任务的逻辑);
  • Thread 提供 "谁来做"(单个线程作为执行载体);
  • ExecutorService 解决 "如何高效地做"(通过线程池管理多个线程,优化资源利用)。
java 复制代码
private static void async1() {
    Runnable task = () -> {
        for (int i = 0; i < 6; i++) {
            System.out.println("异步任务执行中:" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };

    // 启动新线程,异步执行task中的run()方法
    Thread thread = new Thread(task);
    thread.start();
    try {
        // 主线程会等待thread执行完成
        thread.join();
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }

    System.out.println("主线程继续执行...");

    // 创建一个固定大小为2的线程池
    ExecutorService executor = Executors.newFixedThreadPool(2);

    // 提交第一个异步任务
    executor.submit(task);

   // 提交第二个异步任务(复用线程池中的线程)
    executor.submit(() -> {
        try {
            Thread.sleep(1000);
            System.out.println("第二个异步任务执行中");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    });

    try {
        // 等待所有任务完成
        executor.awaitTermination(1, TimeUnit.MINUTES);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    // 关闭线程池
    executor.shutdown();
    System.out.println("线程池已关闭");
}

2. 使用FutureCallable

java 复制代码
// 自定义任务类(实现Callable接口,有返回值)
static class Task implements Callable<String> {
    private String name;
    private long delay;

    public Task(String name, long delay) {
        this.name = name;
        this.delay = delay;
    }

    @Override
    public String call() throws Exception {
        System.out.println(name + "开始执行...");
        Thread.sleep(delay); // 模拟耗时操作
        return name + "执行完成(耗时" + delay + "ms)";
    }
}

private static void async2() {
    // 1. 创建线程池(推荐根据实际需求调整核心线程数)
    ExecutorService executor = Executors.newFixedThreadPool(3); // 固定3个线程的线程池

    try {
        // 2. 提交异步任务(返回Future对象)
        Future<String> future1 = executor.submit(new Task("任务1", 2000));
        Future<Integer> future2 = executor.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                Thread.sleep(1500); // 模拟耗时操作
                return 100 + 200; // 任务结果
            }
        });

        // 3. 主线程可以做其他事情
        System.out.println("主线程执行其他操作...");

        // 4. 获取任务结果(get()方法会阻塞,直到任务完成)
        String result1 = future1.get();
        System.out.println("任务1结果:" + result1);

        Integer result2 = future2.get(3, TimeUnit.SECONDS); // 带超时的get()
        System.out.println("任务2结果:" + result2);

        // 5. 演示取消任务
        Future<String> future3 = executor.submit(new Task("任务3", 3000));
        Thread.sleep(1000); // 等待1秒后取消任务
        boolean isCancelled = future3.cancel(true); // true表示中断正在执行的任务
        System.out.println("任务3是否取消成功:" + isCancelled);
        System.out.println("任务3是否已完成:" + future3.isDone());

    } catch (InterruptedException e) {
        System.out.println("线程被中断:" + e.getMessage());
    } catch (ExecutionException e) {
        System.out.println("任务执行出错:" + e.getMessage());
    } catch (TimeoutException e) {
        System.out.println("获取结果超时:" + e.getMessage());
    } finally {
        // 6. 关闭线程池(必须执行,否则程序不会退出)
        executor.shutdown();
    }
}

3. CompletableFuture (Java 8+)

kotlin 复制代码
// 1. 简单异步任务(无返回值)
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(1);
        System.out.println("任务1:异步执行(无返回值)");
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
});

// 2. 带返回值的异步任务
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(2);
        return "任务2:异步计算结果";
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
});

// 3. 链式操作(处理上一个任务的结果)
CompletableFuture<Integer> future3 = future2
        .thenApply(result -> {
            System.out.println("任务3:处理任务2的结果 -> " + result);
            return result.length(); // 计算字符串长度
        })
        .thenApply(length -> length * 2); // 再做一次处理

// 4. 组合两个异步任务(等待两者完成后处理结果)
CompletableFuture<String> future4 = future2.thenCombine(future3,
        (result2, result3) -> "任务4:组合结果 -> " + result2 + ",长度加倍后:" + result3);

// 5. 多任务并行执行(等待所有任务完成)
CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2, future3);
allFutures.thenRun(() -> System.out.println("所有任务执行完毕!"));

// 6. 异常处理
CompletableFuture<Integer> future5 = CompletableFuture.supplyAsync(() -> {
    if (true) {
        throw new RuntimeException("任务5执行出错!");
    }
    return 100;
}).exceptionally(ex -> {
    System.out.println("捕获异常:" + ex.getMessage());
    return -1; // 异常时返回默认值
});

// 等待所有任务完成(实际开发中很少用get()阻塞,更多用异步回调)
try {
    future5.get();
System.out.println("任务3的最终结果:" + future3.get());
System.out.println("任务4的组合结果:" + future4.get());
} catch (InterruptedException e) {
    throw new RuntimeException(e);
} catch (ExecutionException e) {
    throw new RuntimeException(e);
}

4. 回调

这种方式比较传统了,不推荐使用

java 复制代码
interface Callback {
    void onComplete(String result);
}

void asyncTask(Callback callback) {
    new Thread(() -> {
        try {
            Thread.sleep(1000);
            callback.onComplete("任务完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();
}

// 调用
asyncTask(result -> {
    System.out.println("回调结果:" + result);
});

改用下面的

kotlin 复制代码
CompletableFuture.supplyAsync(() -> {
    // 异步任务
    return "任务结果";
}).thenAccept(result -> {
    // 回调处理结果(非阻塞)
    System.out.println("收到结果: " + result);
}).exceptionally(ex -> {
    // 异常处理
    ex.printStackTrace();
    return null;
});

5. 响应式编程(Reactive Programming)

java 复制代码
// 1. 创建一个简单的数据流(Mono - 单个元素)
Mono<String> mono = Mono.just("Hello Reactor")
        .map(s -> s + "!")
        .doOnNext(System.out::println);

// 2. 创建一个包含多个元素的数据流(Flux)
Flux<Integer> flux = Flux.range(1, 5)
        .map(i -> i * 2)
        .filter(i -> i > 5);

// 3. 异步处理示例
Flux<String> asyncFlux = Flux.just("任务1", "任务2", "任务3")
        .publishOn(Schedulers.boundedElastic()) // 切换到异步线程
        .map(task -> {
            // 模拟耗时操作
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return task + " 处理完成";
        });

// 4. 组合多个数据流
Flux.combineLatest(
        flux.map(i -> "数字: " + i),
        asyncFlux,
        (numStr, taskStr) -> numStr + " - " + taskStr
).subscribe(
        result -> System.out.println("组合结果: " + result),  // 处理正常结果
        error -> System.err.println("错误: " + error.getMessage()),  // 处理错误
        () -> System.out.println("所有数据处理完成")  // 处理完成通知
);

// 等待异步操作完成
try {
    Thread.sleep(5000);
} catch (InterruptedException e) {
    throw new RuntimeException(e);
}

6. ### ​**Java 11+ 异步 HTTP 请求

适合微服务和网络请求场景

java 复制代码
// 1. 创建 HttpClient 实例
HttpClient client = HttpClient.newBuilder()
        .version(HttpClient.Version.HTTP_2)  // 使用 HTTP/2
        .connectTimeout(Duration.ofSeconds(10))  // 连接超时
        .build();

// 2. 创建 GET 请求
HttpRequest getRequest = HttpRequest.newBuilder()
        .uri(URI.create("https://httpbin.org/get"))
        .timeout(Duration.ofSeconds(10))
        .header("Accept", "application/json")
        .GET()
        .build();

// 3. 发送异步 GET 请求
CompletableFuture<HttpResponse<String>> getFuture = client.sendAsync(
        getRequest,
        HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)
);

// 处理 GET 请求结果(非阻塞方式)
getFuture.thenAccept(response -> {
    System.out.println("GET 响应状态码: " + response.statusCode());
    System.out.println("GET 响应体: " + response.body().substring(0, 100) + "..."); // 打印部分内容
}).exceptionally(e -> {
    System.err.println("GET 请求出错: " + e.getMessage());
    return null;
});

// 4. 创建 POST 请求(带表单数据)
HttpRequest postRequest = HttpRequest.newBuilder()
        .uri(URI.create("https://httpbin.org/post"))
        .timeout(Duration.ofSeconds(10))
        .header("Content-Type", "application/x-www-form-urlencoded")
        .POST(HttpRequest.BodyPublishers.ofString("name=test&age=20"))
        .build();

// 5. 发送异步 POST 请求
CompletableFuture<HttpResponse<String>> postFuture = client.sendAsync(
        postRequest,
        HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)
);

// 处理 POST 请求结果(阻塞方式获取,仅作示例)
HttpResponse<String> postResponse = null;
try {
    postResponse = postFuture.get();
} catch (InterruptedException e) {
    throw new RuntimeException(e);
} catch (ExecutionException e) {
    throw new RuntimeException(e);
}
System.out.println("\nPOST 响应状态码: " + postResponse.statusCode());
System.out.println("POST 响应体: " + postResponse.body().substring(0, 100) + "...");

// 等待所有异步操作完成
CompletableFuture.allOf(getFuture, postFuture).join();
System.out.println("\n所有请求处理完毕");

总结

方式 是否推荐 是否阻塞 适用场景 核心工具
Thread / Runnable ⚠️ 简单场景 是(需手动管理) 简单异步任务 Thread, Runnable
ExecutorService + Future ✅ 一般场景 获取结果是阻塞的 需要返回值的异步任务 ExecutorService, Future
CompletableFuture ✅✅ 推荐(Java 8+) 非阻塞 复杂异步逻辑、任务编排 CompletableFuture
回调(Callback)​ ⚠️ 老代码/事件驱动 通常是回调形式 传统异步通知 自定义接口
响应式编程(Reactor/RxJava)​ ✅ 高级场景 非阻塞 流式数据、高并发、WebFlux Mono, Flux, Observable
异步 HTTP 客户端(HttpClient)​ ✅ 网络请求 非阻塞 异步网络调用 HttpClient.sendAsync()
Kotlin 协程(与 Java 互操作)​ ✅ 简洁异步 非阻塞(挂起函数) 更优雅的异步代码 Kotlin Coroutines
相关推荐
程序员鱼皮12 小时前
我代表编程导航,向大家道歉!
前端·后端·程序员
zjjuejin12 小时前
Maven 生命周期与插件机制
后端·maven
阿杆12 小时前
为什么我建议你把自建 Redis 迁移到云上进行托管
redis·后端
Java水解12 小时前
go语言教程(全网最全,持续更新补全)
后端·go
bobz96512 小时前
QEMU 使用 DPDK 时候在 libvirt xml 中设置 sock 的目的
后端
thinktik12 小时前
AWS EKS 计算资源自动扩缩之按需申请Fargate[AWS 中国宁夏区]
后端·aws
thinktik13 小时前
AWS EKS 实现底层EC2计算资源的自动扩缩[AWS 中国宁夏区]
后端·aws
uhakadotcom13 小时前
什么是OpenTelemetry?
后端·面试·github
知其然亦知其所以然13 小时前
MySQL 社招必考题:如何优化特定类型的查询语句?
后端·mysql·面试
用户40993225021213 小时前
给接口加新字段又不搞崩老客户端?FastAPI的多版本API靠哪三招实现?
后端·ai编程·trae