java并发编程 Executors 和 CompletableFuture的区别

327 阅读3分钟

ExecutorsCompletableFuture 是Java中用于并发编程的两个不同工具,它们在用途、实现和使用方式上有显著的区别。

Executors

Executors 是一个工具类,用于创建和管理线程池。它提供了一些静态方法来创建不同类型的线程池,例如固定大小的线程池、缓存线程池、单线程池和定时线程池。

主要特点和用法

  1. 线程池管理

    • Executors.newFixedThreadPool(int nThreads):创建固定大小的线程池。
    • Executors.newCachedThreadPool():创建一个按需创建线程的线程池。
    • Executors.newSingleThreadExecutor():创建一个只有一个线程的线程池。
    • Executors.newScheduledThreadPool(int corePoolSize):创建一个可以调度的线程池。
  2. 任务提交

    • 使用 ExecutorServicesubmit() 方法提交任务,任务可以是 RunnableCallable
    • 线程池负责管理线程的生命周期,任务执行完后可以复用线程。
  3. 示例


ExecutorService executorService = Executors.newFixedThreadPool(5);

executorService.submit(() -> {
    System.out.println("Task is running in thread pool");
});

executorService.shutdown();

CompletableFuture

CompletableFuture 是Java 8引入的一个类,用于处理异步编程。它允许你以非阻塞的方式编写异步代码,并提供了一套丰富的API进行任务的组合、回调和错误处理。

主要特点和用法

  1. 异步任务

    • 使用 CompletableFuture.supplyAsync(Supplier<U> supplier)CompletableFuture.runAsync(Runnable runnable) 提交异步任务。
    • 提供 thenApply(), thenAccept(), thenCompose() 等方法进行任务的组合和回调。
  2. 非阻塞

    • 通过回调方法处理异步任务的结果,避免阻塞主线程。
    • 可以处理异常,例如使用 exceptionally() 方法。
  3. 示例

CompletableFuture.supplyAsync(() -> {
   // 异步任务
   return "Hello";
}).thenApply(result -> {
   // 转换结果
   return result + " World";
}).thenAccept(result -> {
   // 消费结果
   System.out.println(result);
}).exceptionally(ex -> {
   // 处理异常
   System.err.println("Exception: " + ex.getMessage());
   return null;
});

区别总结

  1. 用途

    • Executors 主要用于管理线程池和并发任务的执行,适用于需要显式控制线程池和线程生命周期的场景。
    • CompletableFuture 主要用于处理异步编程,适用于需要进行复杂的异步任务组合、回调和非阻塞处理的场景。
  2. 编程风格

    • Executors 采用传统的线程池管理方式,任务提交后可以使用 Future 获取结果。
    • CompletableFuture 采用函数式编程风格,通过链式调用进行任务的组合和回调处理。
  3. 非阻塞处理

    • 使用 Executors 提交的任务可以通过 Future.get() 获取结果,但这是一个阻塞操作。
    • CompletableFuture 提供了非阻塞的回调方法,例如 thenApply(), thenAccept(),可以在任务完成时进行处理而无需阻塞当前线程。
  4. 错误处理

    • Executors 中的任务错误处理需要显式捕获异常。
    • CompletableFuture 提供了专门的方法进行错误处理,例如 exceptionally()handle()

示例对比

以下是一个示例,展示了如何使用 ExecutorsCompletableFuture 分别实现异步任务:

import java.util.concurrent.*;

public class ExecutorsVsCompletableFuture {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 使用 Executors 管理线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        Future<String> future = executorService.submit(() -> {
            Thread.sleep(1000);
            return "Result from Executors";
        });

        System.out.println("Task submitted with Executors");

        // 使用 CompletableFuture 处理异步任务
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Result from CompletableFuture";
        });

        completableFuture.thenAccept(result -> System.out.println(result));

        // 获取 Executors 的任务结果(这将阻塞当前线程)
        System.out.println(future.get());

        // 关闭线程池
        executorService.shutdown();
    }
}

在这个示例中,Executors 的任务结果通过 Future.get() 获取,这会阻塞当前线程。而 CompletableFuture 通过 thenAccept() 进行回调处理,不会阻塞主线程。