Search code examples
javaandroidfutureexecutorserviceblocking

Java ExecutorService - Monitor Task Completion/Status bar


So I have an ExecutorService successfully blocking and running linearly right now. My trouble is, I am trying to add a status update and I can't figure out how to get Futures to settle one-item at a time. It seems that by the time the first item in my Future<> is ready so is the last. I'm hoping to find a place where I can know how many tasks my executorService has remaining/total so I can calculate a simple percentage indicator. Please note I intend on recycling my Executor and don't want to shut it down.

ExecutorService updateService = Executors.newSingleThreadExecutor();
Callable<String> callHour = () -> {
  //doStuff, unaware of total number of hourCalls
  return "done";
};
private void startMe(int hours){
  List<Future<String>> futureHours;
  List<Callable<String>> hourCalls = new ArrayList<>(hours);
    for (int hour = 0; hour < hours; ++hour) {
      hourCalls.add(callHour); //queue list (not running yet)
    }
    try {
      //executes queue and blocks thread
      futureHours = updateService.invokeAll(hourCalls);
      futureHours.get(0).get();//performs blocking
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

Solution

  • There are two things at work here.


    Firstly, if we take a look at the documentation of ExecutorService#invokeAll(...), we see that it returns

    [...] a list of Futures holding their status and results when all complete. [...]

    (emphasis added by me)

    You most probably want to use Executor#submit(...) instead.


    Secondly, you have no guarantee that the task coupled to futureHours.get(0) is executed first. I would suggest using Future#isDone() with some additional logic:

    private void startMe(int hours) {
      [...]
      try {
        [...]
        ArrayList<Future<String>> futureHoursDone = new ArrayList<>();
        final int numTasks = futureHours.size();
        int done = 0;
        double percentageDone = 0.0d;
        while (futureHours.isEmpty() == false) {
          for (int index = 0; index < futureHours.size(); ++index) {
            Future<String> futureHour = futureHours.get(index);
            if (futureHour.isDone()) {
              futureHours.remove(index);
              futureHoursDone.add(futureHour);
              --index;
              ++done;
              percentageDone = done / (double) numTasks;
            }
          }
        }
      } catch (Exception e) {
        // TODO: don't forget to HCF (https://en.wikipedia.org/wiki/Halt_and_Catch_Fire) :)
        e.printStackTrace();
      }
    }
    

    (This is a rough sketch. To make the progress, i.e. percentage, visible to the outside, you would have to make it an attribute and accessible through, e.g., some getter)