多线程:ExecutorService的submit()方法

郎家岭伯爵 2025年08月04日 13次浏览

前言

之前我们介绍过在多线程的应用中确保所有任务执行完成的代码用法,我们使用 CountDownLatch 计数器来实现。本文我们将使用 ExecutorServicesubmit() 方法来实现。

实现

理论

submit()和execute()

先介绍下 ExecutorServicesubmit()execute() 方法的区别:

  1. 返回值:
    • execute():返回 void,不返回任何结果;
    • submit():返回 Future 对象,可用于获取任务执行结果或取消任务。
  2. 异常处理:
    • execute():任务抛出的未捕获异常会传播到未捕获异常处理器;
    • submit():任务抛出的异常会被捕获并存储在 Future 中,调用 Future.get() 时会重新抛出。
  3. 方法重载:
    • execute():只接受 Runnable
    • submit():有三个重载版本:
      • submit(Runnable task)
      • submit(Runnable task, T result)
      • submit(Callable task)
  4. 任务类型:
    • execute():只能提交Runnable任务;
    • submit():可以提交Runnable和Callable任务。
  5. 使用场景:
    • execute():当不需要任务执行结果,也不关心任务是否成功完成时;
    • submit():当需要获取任务执行结果,或需要处理任务抛出的异常时。

Future.get()

Future.get() 是 Java 并发编程中 Future 接口的核心方法,用于获取异步任务的结果。其主要有两个作用:

  • 阻塞等待:等待异步任务完成;
  • 获取结果:返回任务执行结果(对于 Callable)或 null(对于 Runnable)。

方法签名:

Future 接口提供了两个 get() 方法:

V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
  1. 无参版本 get()
    • 阻塞性:调用后会阻塞当前线程,直到任务完成。
    • 异常处理:
      • 抛出 InterruptedException:如果等待过程中线程被中断;
      • 抛出 ExecutionException:如果任务执行过程中抛出异常;
      • 抛出 CancellationException:如果任务被取消。
  2. 超时版本 get(long timeout, TimeUnit unit)
    • 限时等待:最多等待指定时间;
    • 超时处理:如果在指定时间内任务未完成,抛出 TimeoutException;
    • 其他异常:同样可能抛出 InterruptedException 和 ExecutionException。

实践

这里我们同样来实现模拟多线程环境中所有子线程执行结束后再执行后续逻辑的业务场景:

package org.example.test;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * @author 郎家岭伯爵
 */
public class Test1 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        List<String> list = new ArrayList<>();

        ExecutorService exec = Executors.newFixedThreadPool(8);

        List<Future<Integer>> futures = new ArrayList<>();

        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            int finalI = i;
            Future<Integer> future =  exec.submit(() -> {
                    synchronized (list) {
                        list.add(String.valueOf(finalI));
                    }
                    return list.size();
            });

            futures.add(future);
        }

        for (Future<Integer> future : futures) {
            // 阻塞等待每个任务完成,并检查异常
            future.get();
        }

        exec.shutdown();

        System.out.println(list.size());
        System.out.println("耗时:" + (System.currentTimeMillis() - start));
    }
}

总结

ExecutorService 的 submit() 方法可以返回子线程的执行结果,使用 Future 接收子线程返回后,Future.get() 方法可以起到与 CountDownLatch 相同的效果,阻塞线程等待子线程执行完成。