Java ExecutorService: awaitTermination of all recursively created tasks

后端 未结 11 2114
太阳男子
太阳男子 2020-11-29 03:27

I use an ExecutorService to execute a task. This task can recursively create other tasks which are submitted to the same ExecutorService and those

11条回答
  •  自闭症患者
    2020-11-29 03:59

    (mea culpa: its a 'bit' past my bedtime ;) but here's a first attempt at a dynamic latch):

    package oss.alphazero.sto4958330;
    
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.AbstractQueuedSynchronizer;
    
    public class DynamicCountDownLatch {
        @SuppressWarnings("serial")
        private static final class Sync extends AbstractQueuedSynchronizer {
            private final CountDownLatch toplatch;
            public Sync() {
                setState(0);
                this.toplatch = new CountDownLatch(1);
            }
    
            @Override
            protected int tryAcquireShared(int acquires){
                try {
                    toplatch.await();
                } 
                catch (InterruptedException e) {
                    throw new RuntimeException("Interrupted", e);
                }
                return getState() == 0 ? 1 : -1;
            }
            public boolean tryReleaseShared(int releases) {
                for (;;) {
                    int c = getState();
                    if (c == 0)
                        return false;
                    int nextc = c-1;
                    if (compareAndSetState(c, nextc)) 
                        return nextc == 0;
                }
            }
            public boolean tryExtendState(int acquires) {
                for (;;) {
                    int s = getState();
                    int exts = s+1;
                    if (compareAndSetState(s, exts)) {
                        toplatch.countDown();
                        return exts > 0;
                    }
                }
            }
        }
        private final Sync sync;
        public DynamicCountDownLatch(){
            this.sync = new Sync();
        }
        public void await() 
            throws InterruptedException   
        {
            sync.acquireSharedInterruptibly(1);
        }
    
        public boolean await(long timeout, TimeUnit   unit) 
            throws InterruptedException   
        {
            return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
        }
        public void countDown() {
            sync.releaseShared(1);
        }
        public void join() {
            sync.tryExtendState(1);
        }
    }
    

    This latch introduces a new method join() to the existing (cloned) CountDownLatch API, which is used by tasks to signal their entry into the larger task group.

    The latch is pass around from parent Task to child Task. Each task would, per Suraj's pattern, first 'join()' the latch, do its task(), and then countDown().

    To address situations where the main thread launches the task group and then immediately awaits() -- before any of the task threads have had a chance to even join() -- the topLatch is used int inner Sync class. This is a latch that will get counted down on each join(); only the first countdown is of course significant, as all subsequent ones are nops.

    The initial implementation above does introduce a semantic wrinkle of sorts since the tryAcquiredShared(int) is not supposed to be throwing an InterruptedException but then we do need to deal with the interrupt on the wait on the topLatch.

    Is this an improvement over OP's own solution using Atomic counters? I would say probably not IFF he is insistent upon using Executors, but it is, I believe, an equally valid alternative approach using the AQS in that case, and, is usable with generic threads as well.

    Crit away fellow hackers.

提交回复
热议问题