Java CompletableFuture.anyOf(manyfutures).thenRun(runnable)只运行一次

时间:2014-12-13 18:54:31

标签: java concurrency java-8

我在做

CompletableFuture.anyOf(manyfutures).thenRun( new Runnable() { } }

但是runnable中的代码只运行一次!每当任何期货完成时,我都期待它会多次运行。

每当任何期货完成时,我如何运行一段代码?以最佳方式,这意味着不会:

    public static void append(final CompletableFuture[] futures, Runnable runnable) {
            for (CompletableFuture future : futures) {
                    future.thenRun(runnable);
            }
    }

修改

我正在使用一个ThreadPoolExecutor,我想在执行X个runnables时附加更多的工作。

有没有办法倾听这个并在发生这种情况时提供更多工作?

另一种选择是我在开始时堆叠了数千个工作,但这也不是最佳的。

我在做

... queue = new LinkedBlockingQueue<Runnable>(); 
new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, queue);

2 个答案:

答案 0 :(得分:4)

查看JavaDoc

  

返回任何一个新的CompletableFuture   给定CompletableFutures完成,结果相同。否则,如果   它完成异常,返回的CompletableFuture也是如此   因此,CompletionException将此异常作为原因。如果   没有提供CompletableFutures,返回不完整   CompletableFuture

当给定CompletableFutures任何完成时,该方法返回。即第一个。方法不能多次返回。

要在每个 CompletableFuture之后运行某个操作,只需在每个 theRun上调用thenRunAsyncCompletableFuture。< / p>

如果您有一个List<CompletableFuture<T>>,并且想要一个CompletableFuture<List<T>>,即您希望将一组期货“展开”到一个集合的未来,您可以使用这个技巧:

private static <T> CompletableFuture<List<T>> sequence(List<CompletableFuture<T>> futures) {
    final CompletableFuture<Void> allDoneFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
    return allDoneFuture.thenApply(v ->
                    futures.stream().
                            map(future -> future.join()).
                            collect(toList())
    );
}

取自this article on the usage of CompletableFuture

答案 1 :(得分:1)

这是我追踪这个问题的答案,这个方法需要一些参数才能让人感觉良好:

   /** Basically creates to-start number of futures in a while loop, while passing the index to a Lambda that is passed and that returns a Runnable which will have access to the index. See example below. **/
   public static CompletableFuture[] async(ExecutorService executorService, int start, int to, Runnable beforeAll, Lambda.R1<Runnable, Integer> onEach, Double onPercentage, Runnable onPercentageRun, Runnable afterAll) {
            CompletableFuture[] futures = new CompletableFuture[to-start];

            double        onPercentageIndex = Valid.elvis(onPercentage, 0.0) * futures.length;  // When to onPercentageRun
            AtomicBoolean percentageMet     = new AtomicBoolean ( false );
            AtomicBoolean completeMet       = new AtomicBoolean ( false );
            AtomicInteger complete          = new AtomicInteger ( 0     );

            int i = start;
            if ( i < to && beforeAll != null ) {
                    beforeAll.run();
            }
            boolean percentageSet = onPercentageIndex > 0.0 && onPercentageRun != null;
            boolean completeSet = afterAll != null;
            while( i < to ) {

                    Runnable call = onEach.call(i);
                    futures[i-start] = CompletableFuture.runAsync(

                            () -> {
                                    try {
                                            call.run();
                                    } catch (Throwable e) {
                                            $Log.info(Concurrency.class, "RunAsync: run", e);
                                    }

                                    if ( percentageSet || completeSet ) {
                                            complete.incrementAndGet();

                                            if ( percentageSet && !percentageMet.get() && complete.get() >= onPercentageIndex) {
                                                    percentageMet.set(true);

                                                    try {
                                                            onPercentageRun.run();
                                                    }
                                                    catch(Throwable e) {
                                                            $Log.info(Concurrency.class, "RunAsync: onPercentage", e);
                                                    }
                                            }

                                            if ( completeSet && !completeMet.get() && complete.get() == to ) {
                                                    completeMet.set(true); // Just for clarity, propably redundant

                                                    try {
                                                            afterAll.run();
                                                    }
                                                    catch(Throwable e) {
                                                            $Log.info(Concurrency.class, "RunAsync: onComplete", e);
                                                    }

                                            }
                                    }

                            },

                            executorService
                    );

                    ++i;
            }

            return futures;
    }

有关Lambda.R1的参考,请参阅此Lambda interfaces

该方法可以这样使用:

    private void recursivelyPopulateDataFiles(long fromId) {

            List<Localfile> unproccessed = DB.fetchAllFilesFromId(fromId, limit);

            if ( unproccessed.size() > 0 ) {

                    Concurrency.async(
                            THE_EXECUTOR,

                            0,

                            unproccessed.size(),

                            ITERATIONS_COUNTER_IN_PROGRESS_CAN_BE_USED_BY_OTHERS_TO_LISTEN_GLOBALLY_FOR_WHEN_IT_HITS_ZERO_AGAIN::incrementAndGet,

                            (Integer index) -> () -> {
                                    populateDataFile( unproccessed.get(index) );
                            },

                            0.5,

                            () -> {
                                    recursivelyPopulateDataFiles(unproccessed.get(unproccessed.size() - 1).getId());

                            },

                            ITERATIONS_COUNTER_IN_PROGRESS_CAN_BE_USED_BY_OTHERS_TO_LISTEN_GLOBALLY_FOR_WHEN_IT_HITS_ZERO_AGAIN::decrementAndGet
                    );

            }

    }

当0.5 =未完成大小的50%时,块执行后执行以在执行程序上放置更多。