Java 异步CompletableFuture ExecutorService

使用 Callable 和 Future 处理异步任务

在实际的并发编程中,通常我们会将 Callable 和 Future 一起使用。Future 是一个表示异步任务结果的容器,可以让我们查询任务是否完成、获取结果、取消任务等操作。结合 ExecutorService 来使用时,Callable 可以更方便地执行异步任务

  private static final ExecutorService SHARED_EXECUTOR = Executors.newFixedThreadPool(10);

  /**
   * 检查电话号码是否在黑名单中
   *
   * @param vccId    企业ID
   * @param phone    电话号码
   * @param trunk    中继号码,默认为"0"
   * @param callType 呼叫类型,默认为呼入
   * @return 如果在黑名单中返回true,否则返回false
   */
  @Override
  public boolean isPhoneInBlacklist(long vccId, String phone, String trunk, int callType) {
    // 创建多个检查任务
    List<Callable<Boolean>> tasks = new ArrayList<>();

    // 精确匹配检查
    tasks.add(() -> {
      return false;
    });

    // 全局精确匹配检查(不指定中继号码)
    tasks.add(() -> {
      return false;
    });

    // 并行执行所有检查任务
    return executeParallelChecks(tasks);
  }

  /**
   * 执行并行检查任务
   *
   * @param tasks 检查任务列表
   * @return 如果任何一个任务返回true,则返回true
   */
  private boolean executeParallelChecks(List<Callable<Boolean>> tasks) {
    try {
      List<Future<Boolean>> futures = SHARED_EXECUTOR.invokeAll(tasks);
      for (Future<Boolean> future : futures) {
        if (future.get()) {
          return true;
        }
      }
      return false;
    } catch (InterruptedException e) {
      BLACKLIST_SERVICE_LOG.error("执行黑名单检查时被中断", e);
      Thread.currentThread().interrupt(); // 恢复中断状态
      return false;
    } catch (ExecutionException e) {
      BLACKLIST_SERVICE_LOG.error("执行黑名单检查时发生错误", e);
      return false;
    }
  }
异常处理

Callable 的另一个优势在于它允许抛出受检异常。这使得它在处理可能会抛出异常的任务时更为灵活。在并发环境中处理异常是一个关键问题,尤其是在分布式系统或者 I/O 密集型的操作中。通过使用 Callable,我们可以捕获并处理在任务执行过程中可能抛出的任何异常。

Callable<String> task = () -> {
    if (new Random().nextBoolean()) {
        throw new Exception("任务执行失败");
    }
    return "任务成功";
};

Future<String> future = executor.submit(task);
try {
    String result = future.get();
    System.out.println(result);
} catch (ExecutionException e) {
    System.out.println("任务抛出异常: " + e.getCause());
}

CompletableFuture

CompletableFuture 是 Java 8 引入的一个强大的异步编程工具类,位于 java.util.concurrent 包中。它扩展了 Future 接口,提供了更丰富的功能来处理异步任务和组合多个异步操作。

主要特性

1. 异步执行:

  • 可以通过 supplyAsync()、runAsync() 等方法异步执行任务

2. 链式调用:

  • 支持链式调用和函数组合,如 thenApply()、thenCompose()、thenCombine()
  • 使用 thenApply() 处理所有异步任务完成后的结果收集

3. 组合多个异步操作:

  • allOf() 方法用于等待所有给定的 CompletableFuture 完成
  • anyOf() 方法用于等待任意一个 CompletableFuture 完成

4. 异常处理:

  • 提供了多种异常处理机制,如 exceptionally()、handle() 等

基本使用方法

runAsync与supplyAsync

// 创建一个已完成的 CompletableFuture
CompletableFuture<String> completedFuture = CompletableFuture.completedFuture("Hello");

// 创建一个空的 CompletableFuture
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
    // 执行一些任务
    System.out.println("执行异步任务");
});

// 创建一个有返回值的 CompletableFuture
CompletableFuture<String> futureWithResult = CompletableFuture.supplyAsync(() -> {
    // 执行一些任务并返回结果
    return "异步任务结果";
});
  • supplyAsync(Supplier<U> supplier):异步执行有返回值的任务
  • runAsync(Runnable runnable):异步执行无返回值的任务
  • completedFuture(U value):创建一个已完成的 CompletableFuture

获取结果

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello World");

// 阻塞等待结果
String result = future.get(); // 可能抛出异常

// 或者使用 join() 方法(不抛出受检异常)
String result2 = future.join();

// 设置超时时间
String result3 = future.get(1, TimeUnit.SECONDS);

 回调处理

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");

// 使用 thenAccept 处理结果
future.thenAccept(result -> System.out.println("结果: " + result));

// 使用 thenApply 转换结果
CompletableFuture<String> transformedFuture = future.thenApply(result -> result + " World");

// 使用 thenRun 执行不关心结果的任务
future.thenRun(() -> System.out.println("任务完成"));
  • thenApply(Function<? super T,? extends U> fn):对结果进行转换
  • thenAccept(Consumer<? super T> action):消费结果
  • thenRun(Runnable action):执行不依赖于结果的任务

异常处理

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    if (Math.random() > 0.5) {
        throw new RuntimeException("发生错误");
    }
    return "成功";
});

// 使用 exceptionally 处理异常
CompletableFuture<String> handledFuture = future.exceptionally(ex -> {
    System.out.println("捕获异常: " + ex.getMessage());
    return "默认值";
});

// 使用 handle 处理正常和异常情况
CompletableFuture<String> handledFuture2 = future.handle((result, ex) -> {
    if (ex != null) {
        System.out.println("处理异常: " + ex.getMessage());
        return "默认值";
    }
    return result;
});
  • exceptionally(Function<Throwable, ? extends T> fn):处理异常并提供默认值
  • handle(BiFunction<? super T, Throwable, ? extends U> fn):处理正常和异常情况

组合多个 CompletableFuture

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");

// 使用 thenCombine 组合两个 CompletableFuture 的结果
CompletableFuture<String> combinedFuture = future1.thenCombine(future2, 
    (result1, result2) -> result1 + " " + result2);

// 使用 thenCompose 进行链式异步调用
CompletableFuture<String> composedFuture = future1.thenCompose(result -> 
    CompletableFuture.supplyAsync(() -> result + " World"));
  • thenCompose(Function<? super T,? extends CompletionStage<U>> fn):链式调用
  • thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn):组合两个 CompletableFuture

并行执行多个任务

// 使用 allOf 等待所有任务完成
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "任务1");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "任务2");
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "任务3");

CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2, future3);

// 等待所有任务完成
allFutures.join();

// 使用 anyOf 等待任一任务完成
CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(future1, future2, future3);
Object result = anyFuture.join();
  • allOf(CompletableFuture<?>... cfs):等待所有 CompletableFuture 完成
  • anyOf(CompletableFuture<?>... cfs):等待任一 CompletableFuture 完成

CompletableFuture封装类

import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 异步批量处理器工具类 用于处理批量异步任务,提高处理效率
 *
 * @author hudy
 * @since 1.0
 */
public class AsyncBatchProcessor {

  /**
   * 异步处理批量任务(使用自定义线程池)
   *
   * @param items     待处理的元素列表
   * @param processor 元素处理器
   * @param executor  自定义线程池
   * @param <T>       元素类型
   */
  public static <T> void processAsync(List<T> items, Consumer<T> processor, Executor executor) {
    if (items == null || items.isEmpty()) {
      return;
    }

    // 创建异步任务列表,使用指定的线程池
    List<CompletableFuture<Void>> futures = items.stream()
        .map(item -> CompletableFuture.runAsync(() -> processor.accept(item), executor))
        .collect(Collectors.toList());

    // 等待所有任务完成
    CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
  }

  /**
   * 异步处理批量任务并返回结果(使用自定义线程池)
   *
   * @param items     待处理的元素列表
   * @param processor 元素处理器
   * @param executor  自定义线程池
   * @param <T>       元素类型
   * @param <R>       结果类型
   * @return 处理结果列表
   */
  public static <T, R> List<R> processAsyncWithResultList(List<T> items, Function<T, R> processor,
      Executor executor) {
    if (items == null || items.isEmpty()) {
      return Collections.emptyList();
    }

    // 创建异步任务列表,使用指定的线程池
    List<CompletableFuture<R>> futures = items.stream()
        .map(item -> CompletableFuture.supplyAsync(() -> processor.apply(item), executor))
        .collect(Collectors.toList());

    // 等待所有任务完成并收集结果
    CompletableFuture<Void> allFutures = CompletableFuture.allOf(
        futures.toArray(new CompletableFuture[0]));

    // 返回所有处理结果
    return allFutures.thenApply(v -> futures.stream()
        .map(CompletableFuture::join)
        .collect(Collectors.toList())).join();
  }

  /**
   * 异步处理批量任务(带线程安全结果收集,使用自定义线程池)
   *
   * @param items           待处理的元素列表
   * @param processor       元素处理器
   * @param resultCollector 结果收集器
   * @param executor        自定义线程池
   * @param <T>             元素类型
   * @param <R>             结果类型
   */
  public static <T, R> void processAsyncWithResult(List<T> items,
      ItemProcessor<T, R> processor,
      ResultCollector<R> resultCollector,
      Executor executor) {
    if (items == null || items.isEmpty()) {
      return;
    }

    // 创建异步任务列表,使用指定的线程池
    List<CompletableFuture<Void>> futures = items.stream()
        .map(item -> CompletableFuture.runAsync(() -> {
          R result = processor.process(item);
          if (result != null && resultCollector != null) {
            synchronized (resultCollector) {
              resultCollector.collect(result);
            }
          }
        }, executor))
        .collect(Collectors.toList());

    // 等待所有任务完成
    CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
  }

  /**
   * 异步处理批量任务并返回CompletableFuture列表(使用自定义线程池)
   *
   * @param items     待处理的元素列表
   * @param processor 元素处理器
   * @param executor  自定义线程池
   * @param <T>       元素类型
   * @param <R>       结果类型
   * @return CompletableFuture结果列表
   */
  public static <T, R> List<CompletableFuture<R>> processAsyncFutureList(List<T> items,
      Function<T, R> processor, Executor executor) {
    if (items == null || items.isEmpty()) {
      return Collections.emptyList();
    }

    // 创建异步任务列表,使用指定的线程池
    return items.stream()
        .map(item -> CompletableFuture.supplyAsync(() -> processor.apply(item), executor))
        .collect(Collectors.toList());
  }

  /**
   * 元素处理器接口
   *
   * @param <T> 输入元素类型
   * @param <R> 处理结果类型
   */
  @FunctionalInterface
  public interface ItemProcessor<T, R> {

    /**
     * 处理单个元素
     *
     * @param item 待处理元素
     * @return 处理结果
     */
    R process(T item);
  }

  /**
   * 结果收集器接口
   *
   * @param <R> 结果类型
   */
  @FunctionalInterface
  public interface ResultCollector<R> {

    /**
     * 收集处理结果
     *
     * @param result 处理结果
     */
    void collect(R result);
  }
}

自定义线程池

创建配置类

提供了四种不同类型的线程池,以满足不同业务场景的需求

package net.icsoc.ark.common.aspect;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

/**
 * 自定义线程池配置类 提供多种类型的线程池以满足不同业务场景需求
 */
@Configuration
public class ThreadPoolConfig {

  /**
   * IO密集型任务线程池 适用于文件读写、网络请求等IO密集型操作
   */
  @Bean("ioTaskExecutor")
  public ThreadPoolTaskExecutor ioTaskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    // 核心线程数
    executor.setCorePoolSize(20);
    // 最大线程数
    executor.setMaxPoolSize(100);
    // 队列容量
    executor.setQueueCapacity(200);
    // 线程空闲时间
    executor.setKeepAliveSeconds(60);
    // 线程名称前缀
    executor.setThreadNamePrefix("IO-Task-");
    // 拒绝策略:由调用线程处理该任务
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    // 等待所有任务完成后再关闭线程池
    executor.setWaitForTasksToCompleteOnShutdown(true);
    // 等待时长
    executor.setAwaitTerminationSeconds(60);
    // 初始化
    executor.initialize();
    return executor;
  }

  /**
   * CPU密集型任务线程池 适用于计算密集型任务,如数据处理、复杂算法等
   */
  @Bean("cpuTaskExecutor")
  public ThreadPoolTaskExecutor cpuTaskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    // 核心线程数,通常设置为CPU核心数
    executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
    // 最大线程数
    executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors() * 2);
    // 队列容量
    executor.setQueueCapacity(100);
    // 线程空闲时间
    executor.setKeepAliveSeconds(30);
    // 线程名称前缀
    executor.setThreadNamePrefix("CPU-Task-");
    // 拒绝策略:由调用线程处理该任务
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    // 等待所有任务完成后再关闭线程池
    executor.setWaitForTasksToCompleteOnShutdown(true);
    // 等待时长
    executor.setAwaitTerminationSeconds(60);
    // 初始化
    executor.initialize();
    return executor;
  }

  /**
   * 高并发任务线程池 适用于需要处理大量并发请求的场景
   */
  @Bean("highConcurrentExecutor")
  public ThreadPoolTaskExecutor highConcurrentExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    // 核心线程数
    executor.setCorePoolSize(50);
    // 最大线程数
    executor.setMaxPoolSize(200);
    // 队列容量
    executor.setQueueCapacity(1000);
    // 线程空闲时间
    executor.setKeepAliveSeconds(300);
    // 线程名称前缀
    executor.setThreadNamePrefix("High-Concurrent-");
    // 拒绝策略:由调用线程处理该任务
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    // 等待所有任务完成后再关闭线程池
    executor.setWaitForTasksToCompleteOnShutdown(true);
    // 等待时长
    executor.setAwaitTerminationSeconds(120);
    // 初始化
    executor.initialize();
    return executor;
  }

  /**
   * 定时任务线程池 适用于执行定时任务或延迟任务
   */
  @Bean("scheduledTaskExecutor")
  public ThreadPoolTaskExecutor scheduledTaskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    // 核心线程数
    executor.setCorePoolSize(10);
    // 最大线程数
    executor.setMaxPoolSize(50);
    // 队列容量
    executor.setQueueCapacity(500);
    // 线程空闲时间
    executor.setKeepAliveSeconds(60);
    // 线程名称前缀
    executor.setThreadNamePrefix("Scheduled-Task-");
    // 拒绝策略:由调用线程处理该任务
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    // 等待所有任务完成后再关闭线程池
    executor.setWaitForTasksToCompleteOnShutdown(true);
    // 等待时长
    executor.setAwaitTerminationSeconds(60);
    // 初始化
    executor.initialize();
    return executor;
  }
}

ThreadPoolExecutor ScheduledThreadPoolExecutor使用场景

ThreadPoolExecutor适用于:

  • 需要并发处理大量独立任务
  • 对线程池有精细控制需求
  • 需要自定义拒绝策略、线程工厂等

ScheduledThreadPoolExecutor适用于:

  • 需要延迟执行任务
  • 需要周期性执行任务(如定时检查、轮询等)
  • 需要替代Timer类的功能(Timer只支持单线程)

使用场景

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
// 调度任务,每个任务间隔一定时间执行
Future<String> futureResult = scheduler.schedule(() -> {
    // 任务内容
}, (long) i * grpcDelay, TimeUnit.MILLISECONDS);

这种方式可以控制任务的执行时间,实现批量任务的间隔执行,避免系统过载。

异步自定义线程池结合

ForkJoinPool.commonPool() 是CompletableFuture默认使用的线程池。避免使用默认的 ForkJoinPool,防止任务之间相互影响。CompletableFuture 和 ExecutorService 紧密配合,实现了高效的异步并发处理。这种组合使用方式是 Java 异步编程的最佳实践之一,既保证了性能,又提供了良好的可控性和可维护性。

两者协同工作流程

  • 任务提交:使用 CompletableFuture.supplyAsync() 将任务提交给 ExecutorService
  • 并行执行:ExecutorService 管理线程池,将任务分配给可用线程执行
  • 结果处理:CompletableFuture 提供链式调用和组合操作来处理异步结果
  • 资源管理:使用完成后关闭 ExecutorService

使用场景

  • 1. 微服务并行调用

同时调用多个独立的微服务接口,然后聚合返回结果,提升接口响应速度。

  • 2. 数据报表生成

从多个数据源并行获取统计数据,最后组合生成完整的业务报表。

  • 3. 缓存预热

应用启动时并行加载多个缓存数据,缩短系统预热时间。

  • 4. 批量数据处理

将大量数据分批并行处理,提高数据处理效率

  • 5. 异步事件处理

用户注册后并行发送邮件通知、短信提醒、记录日志等操作。

  • 6. API网关响应聚合

API网关同时调用多个后端服务,聚合响应后统一返回给客户端。

  • 7. 文件上传处理

大文件分块并行上传和处理,提升文件处理速度。

  • 8. 实时数据管道

构建异步数据处理流水线,实现数据的验证、转换、丰富和存储。

  • 9. 第三方接口调用

调用多个第三方API接口并设置超时控制,避免阻塞主线程。

  • 10. 复杂业务流程

将复杂的业务流程拆分为多个并行或串行的异步任务执行。

示例说明

该示例模拟了多个任务的异步执行,并通过 CompletableFuture 和 ExecutorService 来实现并发处理。每个任务会休眠一段时间(模拟耗时操作),然后返回结果。

// 创建自定义线程池
ExecutorService customExecutor = Executors.newFixedThreadPool(5);

try {
    // 测试使用自定义线程池的processAsync方法
    System.out.println("=== 测试使用自定义线程池的processAsync方法 ===");
    List<String> items = Arrays.asList("item1", "item2", "item3", "item4", "item5");
    AsyncBatchProcessor.processAsync(items, item -> {
        // 模拟处理
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("使用自定义线程池处理完成: " + item + ",线程:" + Thread.currentThread().getName());
    }, customExecutor);

    // 测试使用自定义线程池的processAsyncWithResultList方法
    System.out.println("\n=== 测试使用自定义线程池的processAsyncWithResultList方法 ===");
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    List<Integer> results = AsyncBatchProcessor.processAsyncWithResultList(numbers, num -> {
        // 模拟处理
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("处理数字: " + num + ",线程:" + Thread.currentThread().getName());
        return num * num; // 返回平方值
    }, customExecutor);

    System.out.println("处理结果: " + results);

    // 测试使用自定义线程池的processAsyncFutureList方法
    System.out.println("\n=== 测试使用自定义线程池的processAsyncFutureList方法 ===");
    List<CompletableFuture<String>> futures = AsyncBatchProcessor.processAsyncFutureList(items, item -> {
        // 模拟处理
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return "Processed: " + item + " by " + Thread.currentThread().getName();
    }, customExecutor);

    // 等待所有任务完成并收集结果
    List<String> futureResults = futures.stream()
            .map(CompletableFuture::join)
            .collect(Collectors.toList());

    System.out.println("Future处理结果: " + futureResults);
} finally {
    // 关闭线程池
    customExecutor.shutdown();
}

批量入库

  @Autowired
  @Qualifier("scheduledTaskExecutor")
  private ExecutorService fixedExecutorService;

  /**
   * 异步批量写入数据
   *
   * @param hotelList
   * @param billMonth
   */
  public void asyncBatchAddBillMonthlyData(List<BillAccountInfo> hotelList, String billMonth) {
    List<CompletableFuture<Void>> futures = hotelList.stream()
        .map(info -> CompletableFuture.runAsync(() -> {
          try {
            BillMonthly entity = new BillMonthly();
            entity.setHotelId(info.getHotelId());
            entity.setBillMonth(billMonth);
            billMonthlyService.save(entity);
          } catch (Exception e) {
            log.error("异步写入BillMonthly失败,hotelId: {}, 错误信息: {}", info.getHotelId(), e.getMessage(),
                e);
          }
        }, fixedExecutorService))
        .collect(Collectors.toList());

    // 等待所有任务完成
    CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
    // 不需要手动关闭线程池,由Spring容器管理
  }

批量处理限制

控制并发数量,避免系统资源(特别是数据库连接)被耗尽。

// 限制并发数,避免数据库连接池耗尽
final int maxConcurrency = 10;
List<List<Phones>> batches = new ArrayList<>();

// 将trunkList分批处理
for (int i = 0; i < trunkList.size(); i += maxConcurrency) {
  batches.add(trunkList.subList(i, Math.min(i + maxConcurrency, trunkList.size())));
}
// 分批执行删除操作
batches.forEach(batch -> {
  AsyncBatchProcessor.processAsync(batch, item -> {

  });
});

每一批都是临时创建的独立任务集合,当前一批处理完成后,其对应的 CompletableFuture 列表就超出作用域,被垃圾回收

解决线程安全问题

为了解决CompletableFuture的线程安全问题,可以采取以下措施:

  • 避免共享变量:在多个任务之间尽量避免共享变量,使用局部变量或者将变量作为方法参数传递。
  • 使用线程安全的数据结构:如果必须共享变量,可以使用线程安全的数据结构,例如AtomicInteger代替int。
  • 使用同步机制:可以使用synchronized关键字或者Lock接口来保证多个任务的互斥访问。
  • 使用线程安全的 CopyOnWriteArrayList 替代普通的 ArrayList,类似的ConcurrentHashMap等

例子

// 使用线程安全的集合替代普通ArrayList
CopyOnWriteArrayList<String> mixedNums = new CopyOnWriteArrayList<>();
// 使用线程安全的ConcurrentHashMap替代HashMap
ConcurrentHashMap<String,String> listGz = new ConcurrentHashMap<>();

// 使用CompletableFuture并发处理每个号码的删除操作
List<CompletableFuture<Void>> futures = phones.stream()
    .map(phone -> CompletableFuture.runAsync(() -> {
      listGz.put(phone, trunkDeleteDto.getVccCode());
      ccPhoneMapper.deleteByPhone(phone);
      // 查询下线中继号关联其他中继号混显规则的号码
      List<String> relationPhone = ccPhoneMapper.getRelationPhone(phone);
      if (CollectionUtils.isNotEmpty(relationPhone)) {
        mixedNums.addAll(relationPhone);
      }
    }, executorService))
    .collect(Collectors.toList());

// 等待所有异步任务完成
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值