Java CompletableFuture:等待完成百分比

Java CompletableFuture:等待完成百分比,java,parallel-processing,future,percentage,completable-future,Java,Parallel Processing,Future,Percentage,Completable Future,我正在将相同的数据并行写入分布式系统的n个节点。 当成功写入这些节点中的n%时,其余对其他节点的写入并不重要,因为n%保证了其他节点之间的复制 Java的完全未来似乎与我想要的非常接近,例如: CompletableFuture.anyOf() (在第一个未来完成时返回)-避免不必要的等待,但在我需要n%完成时返回得太快 CompletableFuture.allOf() (所有期货完成时返回)-避免过早返回,但不必要地等待100%完成 我正在寻找一种方法,当某个特定百分比的期货交易完成时,

我正在将相同的数据并行写入分布式系统的n个节点。
当成功写入这些节点中的n%时,其余对其他节点的写入并不重要,因为n%保证了其他节点之间的复制

Java的完全未来似乎与我想要的非常接近,例如:

CompletableFuture.anyOf()
(在第一个未来完成时返回)-避免不必要的等待,但在我需要n%完成时返回得太快

CompletableFuture.allOf()
(所有期货完成时返回)-避免过早返回,但不必要地等待100%完成

我正在寻找一种方法,当某个特定百分比的期货交易完成时,我会返回。 例如,如果我提供10个期货,当其中6%或60%成功完成时返回

例如,此功能与

Promise.some(promises, countThatNeedToComplete)

我想知道我是否可以用java中的Dexecutor或vanilla Completable future做类似的事情

我相信你只需要使用Completable future提供的东西就可以实现你想要的,但是你必须实施额外的控制来知道有多少未来的任务已经完成,当达到所需的数量/百分比时,取消剩余的任务

下面是一节课来说明这个想法:

public class CompletableSome<T>
{
private List<CompletableFuture<Void>> tasks;
private int tasksCompleted = 0;

public CompletableSome(List<CompletableFuture<T>> tasks, int percentOfTasksThatMustComplete)
{
    int minTasksThatMustComplete = tasks.size() * percentOfTasksThatMustComplete / 100;
    System.out.println(
        String.format("Need to complete at least %s%% of the %s tasks provided, which means %s tasks.",
            percentOfTasksThatMustComplete, tasks.size(), minTasksThatMustComplete));

    this.tasks = new ArrayList<>(tasks.size());
    for (CompletableFuture<?> task : tasks)
    {
        this.tasks.add(task.thenAccept(a -> {
            // thenAccept will be called right after the future task is completed. At this point we'll
            // check if we reached the minimum number of nodes needed. If we did, then complete the 
            // remaining tasks since they are no longer needed.
            tasksCompleted++;
            if (tasksCompleted >= minTasksThatMustComplete)
            {
                tasks.forEach(t -> t.complete(null));
            }
        }));
    }
}

public void execute()
{
    CompletableFuture.allOf(tasks.toArray(new CompletableFuture<?>[0])).join();
}
}
public类CompletableSome
{
私有列表任务;
私有int tasksCompleted=0;
public CompletableSome(列出任务,必须完成的任务的整数百分比)
{
int minTasksThatMustComplete=tasks.size()*tasksthatMustComplete的百分比/100;
System.out.println(
String.format(“需要完成提供的%s任务中的至少%s%%,这意味着%s个任务。”,
必须完成的任务百分比,tasks.size(),mintasksthatthastcomplete));
this.tasks=newarraylist(tasks.size());
for(可完成的未来任务:任务)
{
this.tasks.add(task.tenaccept)(a->{
//然后在未来任务完成后立即调用Accept
//检查是否达到所需的最小节点数。如果达到,则完成
//剩余任务,因为它们不再需要。
任务已完成++;
如果(任务已完成>=必须完成的最小任务数)
{
tasks.forEach(t->t.complete(null));
}
}));
}
}
public void execute()
{
CompletableFuture.allOf(tasks.toArray(新的CompletableFuture[0])).join();
}
}
您可以使用该类,如下例所示:

public static void main(String[] args)
{
    int numberOfNodes = 4;

    // Create one future task for each node.
    List<CompletableFuture<String>> nodes = new ArrayList<>();
    for (int i = 1; i <= numberOfNodes; i++)
    {
        String nodeId = "result" + i;
        nodes.add(CompletableFuture.supplyAsync(() -> {
            try
            {
                // Sleep for some time to avoid all tasks to complete before the count is checked.
                Thread.sleep(100 + new Random().nextInt(500));
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }

            // The action here is just to print the nodeId, you would make the actual call here.
            System.out.println(nodeId + " completed.");
            return nodeId;
        }));
    }

    // Here we're saying that just 75% of the nodes must be called successfully.
    CompletableSome<String> tasks = new CompletableSome<>(nodes, 75);
    tasks.execute();
}
publicstaticvoidmain(字符串[]args)
{
int numberOfNodes=4;
//为每个节点创建一个未来任务。
列表节点=新的ArrayList();
对于(int i=1;i{
尝试
{
//睡眠一段时间,以避免在检查计数之前完成所有任务。
sleep(100+new Random().nextInt(500));
}
捕捉(中断异常e)
{
e、 printStackTrace();
}
//这里的操作只是打印nodeId,您可以在这里进行实际调用。
System.out.println(nodeId+“完成”);
返回节点ID;
}));
}
//这里我们说的是,只有75%的节点必须被成功调用。
CompletableSome任务=新的CompletableSome(节点,75);
tasks.execute();
}

请注意,使用此解决方案,最终执行的任务可能会超过所需的最小任务数——例如,当两个或多个节点几乎同时响应时,第一个节点响应时,您可能会达到所需的最小计数,但没有时间取消其他任务。如果这是一个问题,那么你必须实现更多的控件。

你可以只看这些方法的源代码,看看它是如何工作的,然后构建自己的逻辑来满足你的需要。看,第2690行和第2784行。谢谢,我在编写自己的实现方面没有问题,但我认为这一定是一个常见的问题,可以通过香草api或库来解决,如果不是这样,我就编写自己的实现。您是否看过
CountDownLatch
?谢谢,这让我走上了正确的道路。我需要返回一个值,如果抛出异常,上面的问题会过早出现,但我已经解决了这些问题。当您从异步作业访问共享资源时,您应该了解线程安全。你不能只做
tasksCompleted++。顺便说一句,我不明白为什么这么多人编写
System.out.println(String.format(…)
而不是
System.out.printf(…)
@holger,正如我在上一段的注释中提到的,是的,很明显,我没有涵盖同步方面。但它们不会影响问题中要求的结果,这就是为什么我不担心它们。