[英]A beginner' question about RejectedExecutionException in Java
最近开始基于Java学习并发,我在windows(jdk 11)上运行如下代码
import java.util.*;
import java.util.concurrent.*;
class TaskWithResult implements Callable<String>{
private int id;
public TaskWithResult(int id){
this.id = id;
}
public String call(){
return "Result of TaskWithResult "+id;
}
}
public class TestCallable{
public static void main(String[] args){
ExecutorService exec = Executors.newCachedThreadPool();
ArrayList<Future<String>> results =
new ArrayList<Future<String>>();
for(int i = 0;i<10;i++){
results.add(exec.submit(new TaskWithResult(i)));
for(Future<String> fs:results){
try{
System.out.println(fs.get());
}catch(InterruptedException e){
System.out.println(e);
return;
}catch(ExecutionException e){
System.out.println(e);
}finally{
exec.shutdown();
}
}
}
}
}
每次运行都会出现sanme异常:
\\output:
Result of TaskWithResult 0
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@380fb434[Not completed, task = me.rexjz.a.TaskWithResult@21bcffb5] rejected from java.util.concurrent.ThreadPoolExecutor@3cda1055[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 1]
at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2055)
at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:825)
at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1355)
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:140)
at javaBin/me.rexjz.a.TestCallable.main(TestCallable.java:22)
代码摘自Thing in Java (4th),我初步猜测是驱动main()的隐式线程在所有任务提交之前执行shutdown()
,因为第一个任务执行成功且异常信息表明pool size= 1
,但这是不可能的,因为 main() 中的所有内容都是按顺序执行的。 所有 Callable 对象都应在关闭前提交。
然后我将 ThreadPool 的类型更改为Executors.newFixedThreadPool(10)
,仍然发生异常并且池大小仍然为 1。
这怎么发生的?
如果您更仔细地查看您的for
循环,您会发现问题(尤其是在代码按常规缩进的情况下):
for (int i = 0; i < 10; i++) {
results.add(exec.submit(new TaskWithResult(i)));
for (Future<String> fs : results) {
try {
System.out.println(fs.get());
} catch (InterruptedException e) {
System.out.println(e);
return;
} catch (ExecutionException e) {
System.out.println(e);
} finally {
exec.shutdown();
}
}
}
请注意,查询每个Future
的for
循环嵌套在提交任务的for
循环中。 这意味着您提交一个任务,等待结果,关闭执行程序,然后尝试提交另一个任务。 以下应该可以解决您的问题:
for (int i = 0; i < 10; i++) {
results.add(exec.submit(new TaskWithResult(i)));
}
executor.shutdown(); // shutdown() allows already-submitted tasks to execute
for (Future<String> fs : results) {
try {
System.out.println(fs.get());
} catch (InterruptedException e) {
e.printStackTrace();
return;
} catch (ExecutionException e) {
e.printStackTrace();
}
}
我移动了executor.shutdown()
调用,因为这只需要发生一次,在您提交最后一个任务之后。 当然,如果您要继续重用执行程序,那么您不会想要关闭它。
我还将System.out.println(e)
更改为e.printStackTrace()
。 通常最好打印堆栈跟踪,而不仅仅是打印异常类型和消息(默认情况下,这是Throwable#toString()
返回的内容)。 在像您的示例这样的短程序中可能并不明显,但堆栈跟踪在更复杂的应用程序中非常有价值,因为它直接指向引发异常的位置。 请参阅什么是堆栈跟踪,以及如何使用它来调试我的应用程序错误? 了解更多信息。
package com.springboot.testapplication;
import java.util.*;
import java.util.concurrent.*;
class TaskWithResult implements Callable<String> {
private int id;
public TaskWithResult(int id) {
this.id = id;
}
public String call() {
return "Result of TaskWithResult " + id;
}
}
public class TestCallable {
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
ArrayList<Future<String>> results = new ArrayList<Future<String>>();
for (int i = 0; i < 10; i++) {
results.add(exec.submit(new TaskWithResult(i)));
}
for (Future<String> fs : results) {
try {
System.out.println(fs.get());
} catch (InterruptedException e) {
System.out.println(e);
return;
} catch (ExecutionException e) {
System.out.println(e);
} finally {
exec.shutdown();
}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.