简体   繁体   中英

A beginner' question about RejectedExecutionException in Java

Recently I begin to learn concurrency based on Java, I run the following code on 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();
          }
        }
        }
      }
    }

The sanme Exception occurs everytime I run it:

\\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)
 

The code is excerpted from Thinging in Java (4th),I initially guess the implicit thread which drives main() execute shutdown() before all the tasks are submitted because the first task is succeessfully executed and the Exeception information indicates that pool size= 1 , but it is impossible because everything in main() is sequentially executed. All the Callable objects should be submitted before shutdown.

Then I change the type of ThreadPool to Executors.newFixedThreadPool(10) , the Exception stil occured and the pool size is still 1.

How did this happen?

If you look at your for loops a little more closely you'll see the problem (especially once the code is conventionally indented):

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

Notice that the for loop which queries each Future is nested within the for loop which submits the tasks. That means you submit one task, wait for the result, shutdown the executor , and then attempt to submit another task. The following should fix your problem:

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

I moved the executor.shutdown() call since that only needs to happen once, after you've submitted the last task. Of course, if you're going to keep reusing the executor then you would not want to shut it down.

I also changed System.out.println(e) to e.printStackTrace() . It's typically better to print the stack trace rather than just the exception type and message (which is what Throwable#toString() returns, by default). It may not be obvious in a short program like your example, but the stack trace is extremely valuable in more complicated applications since it points you directly to where the exception was thrown. See What is a stack trace, and how can I use it to debug my application errors? for more information.

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

    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM