1. 所属接口与方法签名
execute
方法:它是Executor
接口里定义的方法。该接口是 Java 线程池体系中最基础的接口,其核心作用就是执行提交的Runnable
任务。方法签名为void execute(Runnable command)
,意味着它只能接收Runnable
类型的任务,并且没有返回值。submit
方法:属于ExecutorService
接口,这个接口继承自Executor
接口,在功能上进行了扩展。submit
方法有多个重载版本,常见的有Future<?> submit(Runnable task)
、Future<T> submit(Runnable task, T result)
和Future<T> submit(Callable<T> task)
。它既可以接收Runnable
任务,也能接收Callable
任务,并且会返回一个Future
对象用于获取任务执行结果。
2. 任务类型支持
execute
方法:仅支持Runnable
类型的任务。Runnable
接口中的run
方法没有返回值,所以使用execute
提交的任务无法获取执行结果。submit
方法:除了支持Runnable
任务外,还支持Callable
任务。Callable
接口中的call
方法可以有返回值,使用submit
提交Callable
任务时,能通过返回的Future
对象获取任务执行的结果。
3. 异常处理
execute
方法:如果任务在执行过程中抛出异常,这个异常会直接抛到调用线程池的线程中。如果没有进行异常捕获和处理,可能会导致线程池中的线程终止。submit
方法:当任务抛出异常时,异常不会立即抛出,而是被封装在返回的Future
对象中。只有当调用Future
对象的get
方法时,才会抛出异常。这样可以更方便地对异常进行统一处理。
4. 返回值
execute
方法:没有返回值,只是单纯地将任务提交给线程池执行,无法得知任务的执行状态和结果。submit
方法:返回一个Future
对象,通过这个对象可以检查任务是否完成、取消任务以及获取任务的执行结果。可以使用Future
的isDone
方法判断任务是否完成,使用get
方法阻塞等待任务执行完成并获取结果。
示例代码对比
使用 execute
方法
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecuteExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
System.out.println("Running a Runnable task using execute.");
});
executor.shutdown();
}
}
使用 submit
方法
import java.util.concurrent.*;
public class SubmitExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(() -> {
System.out.println("Running a Callable task using submit.");
return "Task result";
});
String result = future.get();
System.out.println("Task result: " + result);
executor.shutdown();
}
}
当你只需要提交一个不需要返回结果的 Runnable
任务,并且希望在任务出现异常时能及时处理,使用 execute
方法更合适;而当你需要提交 Callable
任务获取执行结果,或者想要更灵活地处理任务的执行状态和异常时,submit
方法是更好的选择。