Search code examples
javamultithreadingthreadpoolexecutor

How can I check if all tasks have been completed (normally or abruptly)?


I have the following class:

public class Service{
    private static final ExecutorService executor = Executors.newFixedThreadPool(4);

    public synchronized void execute(Collection<Runnable> runs){
        for(Runnable r: runs){
            executor.execute(r);
        }
    }

    public boolean isComplete(){
        //should return true iff all tasks applied by the execute(Collection<Runnable> runs) 
        //method're finished their execution (normally or abruptly, 
        //it doesn matter)
    }
}

How can I implement the isComplete() method. I need to check if there's a task that is currently in progress. If the executor is cleared(all tasks are completed) then the method should return true, otherwise return false.


Solution

  • Given that you are using ExecutorService you could use submit instead of execute and store the returned Future in a list. Then inside isComplete() iterate through all of the Futures and call isDone() on each one. (This approach will also let you cancel the submitted tasks if required, through the Futures).

    For example :

    class Service{
        private static final ExecutorService executor = Executors.newFixedThreadPool(4);
        private List<Future<?>> futures;
        public void execute(Collection<Runnable> runs){
            futures = runs.stream()
                    .map(r -> executor.submit(r))
                    .collect(Collectors.toList());
        }
    
        public boolean isComplete(){
            for (Future<?> future : futures) 
                if (!future.isDone()) return false;
    
            return true;
        }
    }
    

    Depending on your use case, you may get better performance by removing items from the futures list as you go, but you (may) need to sync the isComplete method:

        public synchronized boolean isComplete(){
            Iterator<Future<?>> itr = futures.iterator();
            while (itr.hasNext()) {
                if (itr.next().isDone()) itr.remove();
                else return false;
            }
            return true;
        }
    

    As this code sample is written, it assumes that you will only make one call to execute per instance of Service, so it does not need to be synchronized. If you will have multiple concurrent callers to execute on each Service instance then note that this will replace the futures list on each call to execute. You could handle that by making the class single-use only, or by appending to futures. It depends entirely on your use-case.