java中的线程池是一个非常重要的多线程编程工具,它可以通过预先创建一组线程并维护着队列来管理并发很高的任务。在使用线程池时,开发人员可以使用两个主要的方法submit()和execute()提交任务。
线程池中submit()和execute()方法的区别主要体现在返回值、异常处理、任务类型支持以及任务处理方式等方面。
-
返回值:
- execute()方法:没有返回值。它主要用于提交不需要返回结果的Runnable任务。
- submit()方法:可以提交Runnable或Callable任务,并返回一个Future对象。通过Future对象,可以获取任务的执行结果、判断任务是否完成、取消任务执行以及获取任务执行中抛出的异常。
-
异常处理:
- execute()方法:如果任务执行过程中抛出未捕获的异常,异常会被传递给线程池的UncaughtExceptionHandler,开发者需要手动在任务内部捕获异常,否则异常会丢失。
- submit()方法:如果任务执行过程中抛出异常,异常会被封装在Future对象中,调用Future.get()时会抛出ExecutionException。开发者可以通过Future.get()捕获并处理异常.
-
任务类型支持:
- execute()方法:只能接收Runnable类型的任务,Runnable任务没有返回值。
- submit()方法:既可以接收Runnable任务,也可以接收Callable任务。Callable任务可以返回结果。
-
任务处理方式:
- execute()方法:提交的任务直接在调用线程(通常是主线程)中运行,适用于不需要返回结果且希望立即执行的场景。
- submit()方法:提交的任务会被添加到阻塞队列中,由线程池中的工作线程处理。提交任务的线程可以继续执行其他任务,直到任务执行完毕并返回结果。
用伪代码模拟一下线程池抛异常的场景:
public class ThreadPoolException {
public static void main(String[] args) {
//创建一个线程池
ExecutorService executorService= Executors.newFixedThreadPool(1);
//当线程池抛出异常后 submit无提示,其他线程继续执行
executorService.submit(new task());
//当线程池抛出异常后 execute抛出异常,其他线程继续执行新任务
executorService.execute(new task());
}
}
//任务类
class task implements Runnable{
@Override
public void run() {
System.out.println("进入了task方法!!!");
int i=1/0;
}
}
运行结果:
submit()
想要获取异常信息就必须使用get()
方法!!
//当线程池抛出异常后 submit无提示,其他线程继续执行
Future<?> submit = executorService.submit(new task());
submit.get();
2. 如何获取和处理异常
方案一:使用 try -catch
public class ThreadPoolException {
public static void main(String[] args) {
//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(1);
//当线程池抛出异常后 submit无提示,其他线程继续执行
executorService.submit(new task());
//当线程池抛出异常后 execute抛出异常,其他线程继续执行新任务
executorService.execute(new task());
}
}
// 任务类
class task implements Runnable {
@Override
public void run() {
try {
System.out.println("进入了task方法!!!");
int i = 1 / 0;
} catch (Exception e) {
System.out.println("使用了try -catch 捕获异常" + e);
}
}
}
打印结果:
可以看到 submit 和 execute都清晰易懂的捕获到了异常,可以知道我们的任务出现了问题,而不是消失的无影无踪。
方案二:使用Thread.setDefaultUncaughtExceptionHandler
方法捕获异常
UncaughtExceptionHandler
是Thread类一个内部类,也是一个函数式接口。
内部的uncaughtException
是一个处理线程内发生的异常的方法,参数为线程对象t和异常对象e。
方案三:重写afterExecute进行异常处理
在excute的方法里面,可以通过重写afterExecute
进行异常处理,但是注意! 这个也只适用于excute提交