线程池遇到未处理的异常会崩溃吗?
线程池中的 execute
和 submit
方法详解
目录
- 引言
- execute 方法
- 使用示例代码
- submit 方法
- 2.1 提交 Callable 任务
- 2.2 提交 Runnable 任务
- 遇到未处理异常
- 3.1 execute 方法遇到未处理异常
- 3.2 submit 方法遇到未处理异常
- 小结
引言
在多线程编程中,线程池是提高性能和资源利用率的重要工具。Java 提供了 execute
和 submit
两种方法来提交任务到线程池。虽然它们看起来相似,但在实际使用中却有显著的区别。本文将详细介绍这两种方法的使用场景及其在处理异常时的行为差异。
execute 方法
execute
方法用于提交一个不需要返回值的任务给线程池执行。它接收一个 Runnable
类型的参数,并且不返回任何结果。
使用示例代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecuteDemo {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);
// 使用 execute 方法提交任务
executor.execute(() -> {
System.out.println("Task running in " + Thread.currentThread().getName());
try {
// 模拟任务执行
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Task was interrupted");
}
System.out.println("Task finished");
});
// 关闭线程池
executor.shutdown();
}
}
说明:execute
方法适用于那些只需要执行任务,而不需要关心任务执行结果的场景。
submit 方法
submit
方法用于提交一个需要返回值的任务(Callable
对象),或者不需要返回值但希望获取任务状态的任务(Runnable
对象)。它接收一个 Callable
或 Runnable
类型的参数,并返回一个 Future
对象,通过该对象可以获取任务的执行结果或检查任务的状态。
2.1 提交 Callable 任务
示例代码:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class SubmitCallableDemo {
public static void main(String[] args) {
// 创建一个固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
// 提交一个 Callable 任务给线程池执行
Future<String> future = executorService.submit(() -> {
Thread.sleep(2000); // 模拟任务执行时间
return "Task's execution result";
});
try {
// 获取任务的执行结果
String result = future.get();
System.out.println("Task result: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
// 关闭线程池
executorService.shutdown();
}
}
说明:submit
方法适用于需要获取任务执行结果的场景,通过 Future
对象可以方便地获取返回值。
2.2 提交 Runnable 任务
示例代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class SubmitRunnableDemo {
public static void main(String[] args) {
// 创建一个固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
// 提交一个 Runnable 任务给线程池执行,并获取一个 Future 对象
Future<?> future = executorService.submit(() -> {
System.out.println("Task is running in thread: " + Thread.currentThread().getName());
});
// 检查任务是否完成(这里只是为了示例,实际使用中可能不需要这样做)
if (future.isDone()) {
System.out.println("Task is done");
} else {
System.out.println("Task is not done yet");
}
// 关闭线程池
executorService.shutdown();
}
}
说明:即使提交的是 Runnable
任务,submit
方法也会返回一个 Future
对象,可以用来检查任务的状态。
遇到未处理异常
线程池在遇到未处理异常时的行为与添加任务的方法有关,execute
方法和 submit
方法的行为是不同的。
3.1 execute 方法遇到未处理异常
示例代码:
import java.util.concurrent.*;
public class ThreadPoolExecutorExceptionTest {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1,
1,
1000,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(100)
);
// 添加任务一
executor.execute(() -> {
String tName = Thread.currentThread().getName();
System.out.println("线程名:" + tName);
throw new RuntimeException("抛出异常");
});
// 添加任务二
executor.execute(() -> {
String tName = Thread.currentThread().getName();
System.out.println("线程名:" + tName);
throw new RuntimeException("抛出异常");
});
}
}
执行结果:
线程名:pool-1-thread-1
线程名:pool-1-thread-2
说明:
- 当使用
execute
方法时,如果任务抛出未捕获的异常,线程会崩溃并打印异常信息,但不会影响其他线程的运行。 - 在上述代码中,两个任务分别在不同的线程中执行,即使任务抛出异常,线程池仍然可以正常运行。
3.2 submit 方法遇到未处理异常
示例代码:
import java.util.concurrent.*;
public class ThreadPoolExecutorExceptionTest {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1,
1,
1000,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(100)
);
// 添加任务一
Future<?> future = executor.submit(() -> {
String tName = Thread.currentThread().getName();
System.out.println("线程名:" + tName);
throw new RuntimeException("抛出异常");
});
// 添加任务二
Future<?> future2 = executor.submit(() -> {
String tName = Thread.currentThread().getName();
System.out.println("线程名:" + tName);
throw new RuntimeException("抛出异常");
});
try {
future.get();
} catch (Exception e) {
System.out.println("遇到异常:" + e.getMessage());
}
try {
future2.get();
} catch (Exception e) {
System.out.println("遇到异常:" + e.getMessage());
}
}
}
执行结果:
线程名:pool-1-thread-1
遇到异常:java.lang.RuntimeException: 抛出异常
线程名:pool-1-thread-1
遇到异常:java.lang.RuntimeException: 抛出异常
说明:
- 当使用
submit
方法时,任务抛出的异常会被封装在Future
的get
方法中,而不会直接影响执行任务的线程。 - 线程池中的线程可以继续复用,不会因为任务抛出异常而崩溃。
小结
线程池在遇到未处理的异常时,不同添加任务的方法的执行行为是不同的:
execute
方法:遇到未处理的异常,线程会崩溃,并打印异常信息。submit
方法:遇到未处理的异常,线程本身不会受到影响(线程可以复用),只是将异常信息封装到返回的对象Future
中。
理解这两种方法的区别,可以帮助我们在实际开发中更好地选择适合的线程池任务提交方式