Executors: How to synchronously wait until all tasks have finished if tasks are created recursively?

前端 未结 9 1492
傲寒
傲寒 2020-12-31 09:37

My question is strongly related to this one here. As was posted there, I would like the main thread to wait until the work queue is empty and all tasks have finished. The pr

9条回答
  •  死守一世寂寞
    2020-12-31 10:15

    This one was actually rather interesting problem to solve. I must warn that I have not tested the code fully.

    The idea is to simply track the task execution:

    • if task is successfully queued, counter is incremented by one
    • if task is cancelled and it has not been executed, counter is decremented by one
    • if task has been executed, counter is decremented by one

    When shutdown is called and there are pending tasks, delegate will not call shutdown on the actual ExecutorService. It will allow queuing new tasks until pending task count reaches zero and shutdown is called on actual ExecutorService.

    public class ResilientExecutorServiceDelegate implements ExecutorService {
        private final ExecutorService executorService;
        private final AtomicInteger pendingTasks;
        private final Lock readLock;
        private final Lock writeLock;
        private boolean isShutdown;
    
        public ResilientExecutorServiceDelegate(ExecutorService executorService) {
            ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
            this.pendingTasks = new AtomicInteger();
            this.readLock = readWriteLock.readLock();
            this.writeLock = readWriteLock.writeLock();
            this.executorService = executorService;
            this.isShutdown = false;
        }
    
        private  T addTask(Callable task) {
            T result;
            boolean success = false;
            // Increment pending tasks counter
            incrementPendingTaskCount();
            try {
                // Call service
                result = task.call();
                success = true;
            } catch (RuntimeException exception) {
                throw exception;
            } catch (Exception exception) {
                throw new RejectedExecutionException(exception);
            } finally {
                if (!success) {
                    // Decrement pending tasks counter
                    decrementPendingTaskCount();
                }
            }
            return result;
        }
    
        private void incrementPendingTaskCount() {
            pendingTasks.incrementAndGet();
        }
    
        private void decrementPendingTaskCount() {
            readLock.lock();
            if (pendingTasks.decrementAndGet() == 0 && isShutdown) {
                try {
                    // Shutdown
                    executorService.shutdown();
                } catch (Throwable throwable) {
                }
            }
            readLock.unlock();
        }
    
        @Override
        public void execute(final Runnable task) {
            // Add task
            addTask(new Callable() {
                @Override
                public Object call() {
                    executorService.execute(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                task.run();
                            } finally {
                                decrementPendingTaskCount();
                            }
                        }
                    });
                    return null;
                }
            });
        }
    
        @Override
        public boolean awaitTermination(long timeout, TimeUnit unit)
                throws InterruptedException {
            // Call service
            return executorService.awaitTermination(timeout, unit);
        }
    
        @Override
        public  List> invokeAll(Collection> tasks)
                throws InterruptedException {
            // It's ok to increment by just one
            incrementPendingTaskCount();
            try {
                return executorService.invokeAll(tasks);
            } finally {
                decrementPendingTaskCount();
            }
        }
    
        @Override
        public  List> invokeAll(
                Collection> tasks, long timeout, TimeUnit unit)
                throws InterruptedException {
            // It's ok to increment by just one
            incrementPendingTaskCount();
            try {
                return executorService.invokeAll(tasks, timeout, unit);
            } finally {
                decrementPendingTaskCount();
            }
        }
    
        @Override
        public  T invokeAny(Collection> tasks)
                throws InterruptedException, ExecutionException {
            // It's ok to increment by just one
            incrementPendingTaskCount();
            try {
                return executorService.invokeAny(tasks);
            } finally {
                decrementPendingTaskCount();
            }
        }
    
        @Override
        public  T invokeAny(Collection> tasks,
                long timeout, TimeUnit unit) throws InterruptedException,
                ExecutionException, TimeoutException {
            incrementPendingTaskCount();
            try {
                return executorService.invokeAny(tasks, timeout, unit);
            } finally {
                decrementPendingTaskCount();
            }
        }
    
        @Override
        public boolean isShutdown() {
            return isShutdown;
        }
    
        @Override
        public boolean isTerminated() {
            return executorService.isTerminated();
        }
    
        @Override
        public void shutdown() {
            // Lock write lock
            writeLock.lock();
            // Set as shutdown
            isShutdown = true;
            try {
                if (pendingTasks.get() == 0) {
                    // Real shutdown
                    executorService.shutdown();
                }
            } finally {
                // Unlock write lock
                writeLock.unlock();
            }
        }
    
        @Override
        public List shutdownNow() {
            // Lock write lock
            writeLock.lock();
            // Set as shutdown
            isShutdown = true;
            // Unlock write lock
            writeLock.unlock();
    
            return executorService.shutdownNow();
        }
    
        @Override
        public  Future submit(final Callable task) {
            // Create execution status
            final FutureExecutionStatus futureExecutionStatus = new FutureExecutionStatus();
            // Add task
            return addTask(new Callable>() {
                @Override
                public Future call() {
                    return new FutureDelegate(
                            executorService.submit(new Callable() {
                                @Override
                                public T call() throws Exception {
                                    try {
                                        // Mark as executed
                                        futureExecutionStatus.setExecuted();
                                        // Run the actual task
                                        return task.call();
                                    } finally {
                                        decrementPendingTaskCount();
                                    }
                                }
                            }), futureExecutionStatus);
                }
            });
        }
    
        @Override
        public Future submit(final Runnable task) {
            // Create execution status
            final FutureExecutionStatus futureExecutionStatus = new FutureExecutionStatus();
            // Add task
            return addTask(new Callable>() {
                @Override
                @SuppressWarnings("unchecked")
                public Future call() {
                    return new FutureDelegate(
                            (Future) executorService.submit(new Runnable() {
                                @Override
                                public void run() {
                                    try {
                                        // Mark as executed
                                        futureExecutionStatus.setExecuted();
                                        // Run the actual task
                                        task.run();
                                    } finally {
                                        decrementPendingTaskCount();
                                    }
                                }
                            }), futureExecutionStatus);
                }
            });
        }
    
        @Override
        public  Future submit(final Runnable task, final T result) {
            // Create execution status
            final FutureExecutionStatus futureExecutionStatus = new FutureExecutionStatus();
            // Add task
            return addTask(new Callable>() {
                @Override
                public Future call() {
                    return new FutureDelegate(executorService.submit(
                            new Runnable() {
                                @Override
                                public void run() {
                                    try {
                                        // Mark as executed
                                        futureExecutionStatus.setExecuted();
                                        // Run the actual task
                                        task.run();
                                    } finally {
                                        decrementPendingTaskCount();
                                    }
                                }
                            }, result), futureExecutionStatus);
                }
            });
        }
    
        private class FutureExecutionStatus {
            private volatile boolean executed;
    
            public FutureExecutionStatus() {
                executed = false;
            }
    
            public void setExecuted() {
                executed = true;
            }
    
            public boolean isExecuted() {
                return executed;
            }
        }
    
        private class FutureDelegate implements Future {
            private Future future;
            private FutureExecutionStatus executionStatus;
    
            public FutureDelegate(Future future,
                    FutureExecutionStatus executionStatus) {
                this.future = future;
                this.executionStatus = executionStatus;
            }
    
            @Override
            public boolean cancel(boolean mayInterruptIfRunning) {
                boolean cancelled = future.cancel(mayInterruptIfRunning);
                if (cancelled) {
                    // Lock read lock
                    readLock.lock();
                    // If task was not executed
                    if (!executionStatus.isExecuted()) {
                        decrementPendingTaskCount();
                    }
                    // Unlock read lock
                    readLock.unlock();
                }
                return cancelled;
            }
    
            @Override
            public T get() throws InterruptedException, ExecutionException {
                return future.get();
            }
    
            @Override
            public T get(long timeout, TimeUnit unit) throws InterruptedException,
                    ExecutionException, TimeoutException {
                return future.get(timeout, unit);
            }
    
            @Override
            public boolean isCancelled() {
                return future.isCancelled();
            }
    
            @Override
            public boolean isDone() {
                return future.isDone();
            }
        }
    }
    
        

    提交回复
    热议问题