关于Java中RejectedExecutionException的初学者问题

问题描述 投票:0回答:3

最近开始学习基于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)
 

代码摘自Thinging in Java(4th),我初步猜测是在所有任务提交之前驱动main()执行的隐式线程

shutdown()
,因为第一个任务执行成功并且异常信息表明
 pool size= 1
,但这是不可能的,因为 main() 中的所有内容都是顺序执行的。所有 Callable 对象都应该在关闭之前提交。

然后我把ThreadPool的类型改为

Executors.newFixedThreadPool(10)
,还是出现了Exception,池大小还是1。

这是怎么发生的?

java multithreading
3个回答
2
投票

如果你仔细观察你的

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();
    }
  }
}

请注意,查询每个

for
Future
循环是 nested 于提交任务的
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()
返回的内容)。在像您的示例这样的短程序中,它可能并不明显,但堆栈跟踪在更复杂的应用程序中非常有价值,因为它直接指向抛出异常的位置。请参阅什么是堆栈跟踪,以及如何使用它来调试应用程序错误?了解更多信息。


0
投票
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();
            }
        }

    }
}

0
投票

在shutDown()或awaitTermination()之前编写invokeAny()或invokeAll()方法。

 Integer result  = executorService.invokeAny(futureList);
 System.out.println(result);
 executorService.shutdown();
© www.soinside.com 2019 - 2024. All rights reserved.