Java 向fork-join递归添加停止条件

Java 向fork-join递归添加停止条件,java,recursion,fork-join,Java,Recursion,Fork Join,为了简化我的案例,假设我正在使用Java的Fork-Join框架实现一个二进制搜索。我的目标是在整数数组中找到一个特定的整数值(目标整数)。这可以通过将数组拆分一半来完成,直到它足够小,可以执行串行搜索。算法的结果需要是一个布尔值,指示是否在数组中找到目标整数 幻灯片28之后将探讨类似的问题。然而,Kreft的目标是找到数组中最大的数字,因此必须扫描所有条目。在我的例子中,没有必要扫描整个数组,因为一旦找到目标整数,搜索就可以停止 我的问题是,一旦遇到目标整数,许多任务已经插入到线程池中,我需要

为了简化我的案例,假设我正在使用Java的Fork-Join框架实现一个二进制搜索。我的目标是在整数数组中找到一个特定的整数值(目标整数)。这可以通过将数组拆分一半来完成,直到它足够小,可以执行串行搜索。算法的结果需要是一个布尔值,指示是否在数组中找到目标整数

幻灯片28之后将探讨类似的问题。然而,Kreft的目标是找到数组中最大的数字,因此必须扫描所有条目。在我的例子中,没有必要扫描整个数组,因为一旦找到目标整数,搜索就可以停止

我的问题是,一旦遇到目标整数,许多任务已经插入到线程池中,我需要取消它们,因为继续搜索没有意义。我试图从递归任务内部调用getPool().terminate(),但这没有多大帮助,因为许多任务已经排队,我甚至注意到,即使在调用shutdown之后,新的ONCE也在排队

我目前的解决方案是使用一个静态的volatile boolean,它被初始化为“false”,并在任务开始时检查它的值。如果仍然为“false”,则任务开始工作,如果为“true”,则任务立即返回。我实际上可以使用递归操作来实现这一点

所以我认为这个解决方案应该是可行的,但是我想知道这个框架是否提供了一些标准的方法来处理类似的情况——例如,为递归定义一个停止条件,从而取消所有排队的任务

请注意,如果我想在找到目标整数时立即停止所有正在运行的任务(其中一个正在运行的任务),我必须检查这些任务中每行后面的布尔值,这可能会影响性能,因为无法缓存该布尔值(它被定义为volatile)

因此,我认为确实需要一些标准的解决方案,可以以清除队列和中断正在运行的任务的形式提供。但我还没有找到这样的解决方案,我想知道是否有其他人知道或者有更好的想法

谢谢你抽出时间, 阿萨夫

编辑:这是我的测试代码:

package xxx;

import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

public class ForkJoinTest {

    static final int ARRAY_SIZE = 1000;
    static final int THRESHOLD = 10;

    static final int MIN_VALUE = 0;
    static final int MAX_VALUE = 100;

    static Random rand = new Random();


    // a function for retrieving a random int in a specific range
    public static int randInt(int min, int max) {
        return rand.nextInt((max - min) + 1) + min;
    }

    static volatile boolean result = false;
    static int[] array = new int[ARRAY_SIZE];
    static int target;

    @SuppressWarnings("serial")
    static class MyAction extends RecursiveAction {

        int startIndex, endIndex;

        public MyAction(int startIndex, int endIndex) {
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }

        // if the target integer was not found yet: we first check whether 
        // the entries to search are too few. In that case, we perform a 
        // sequential search and update the result if the target was found. 
        // Otherwise, we break the search into two parts and invoke the 
        // search in these two tasks.
        @Override
        protected void compute() {
            if (!result) {
                if (endIndex-startIndex<THRESHOLD) { 
                    // 
                    for (int i=startIndex ; i<endIndex ; i++) {
                        if (array[i]==target) {
                            result = true;
                        }
                    }
                } else {
                    int middleIndex = (startIndex + endIndex) / 2;
                    RecursiveAction action1 = new MyAction(startIndex, middleIndex);
                    RecursiveAction action2 = new MyAction(middleIndex+1, endIndex);
                    invokeAll(Arrays.asList(action1,action2));
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        for (int i=0 ; i<ARRAY_SIZE ; i++) {
            array[i] = randInt(MIN_VALUE, MAX_VALUE);
        }
        target = randInt(MIN_VALUE, MAX_VALUE);
        ForkJoinPool pool = new ForkJoinPool();
        pool.invoke(new MyAction(0,ARRAY_SIZE));
        System.out.println(result);
    }

}
xxx包;
导入java.util.array;
导入java.util.Random;
导入java.util.concurrent.ExecutionException;
导入java.util.concurrent.ForkJoinPool;
导入java.util.concurrent.RecursiveAction;
公共类测试{
静态最终整数数组_SIZE=1000;
静态最终整数阈值=10;
静态最终整数最小值=0;
静态最终int最大值=100;
静态随机兰德=新随机();
//用于检索特定范围内的随机整数的函数
公共静态整数和整数(整数最小值,整数最大值){
返回rand.nextInt((最大-最小)+1)+min;
}
静态可变布尔结果=false;
静态int[]数组=新int[array_SIZE];
静态int目标;
@抑制警告(“串行”)
静态类MyAction扩展了RecursiveAction{
int startIndex,endIndex;
公共MyAction(int startIndex、int endIndex){
this.startIndex=startIndex;
this.endIndex=endIndex;
}
//如果还没有找到目标整数:我们首先检查
//要搜索的条目太少。在这种情况下,我们执行
//如果找到目标,则按顺序搜索并更新结果。
//否则,我们将搜索分为两部分并调用
//搜索这两个任务。
@凌驾
受保护的void compute(){
如果(!结果){

如果(endIndex startIndex我想你可能是在为正确的解决方案发明障碍

您说您的
boolean stop
标志必须是
volatile
,因此会干扰解决方案的速度-是和否-访问
volatile
确实会进行缓存刷新,但您是否考虑过
原子布尔

我认为正确的解决方案是使用
AtomicBoolean
标志来停止所有进程。您应该以合理的细粒度方式检查,以使系统快速停止

试图清除所有队列并中断所有线程是错误的——这将导致可怕的混乱

    static AtomicBoolean finished = new AtomicBoolean();
    ....

        protected void compute() {
            if (!finished.get()) {
                if (endIndex - startIndex < THRESHOLD) {
                    //
                    for (int i = startIndex; i < endIndex && !finished.get(); i++) {
                        if (array[i] == target) {
                            finished.set(true);
                            System.out.print("Found at " + i);
                        }
                    }
                } else {
                    ...
                }
            }
        }
static AtomicBoolean finished=新的AtomicBoolean();
....
受保护的void compute(){
如果(!finished.get()){
if(结束索引-开始索引<阈值){
//
对于(int i=startIndex;i
我在上面留下了一条关于如何做到这一点的评论,我看了一个在许多内置函数中实现这一点的开源产品。让我在这里介绍一些细节

如果要取消正在开始或当前正在执行的任务,则每个任务都需要了解每个其他任务。当一个任务找到它想要的内容时,该任务需要通知每个其他任务停止。使用二元递归除法(RecursiveTask等)无法做到这一点由于您以递归方式创建新任务,而旧任务永远不会知道新任务。我相信您可以将对“停止我”字段的引用传递给每个新任务,但这将变得非常混乱,调试将是“有趣的”

您可以使用Java8 CountedCompleter()实现这一点。该框架是为了支持该类而设计的,因此框架应该手动完成的事情需要手动完成,但它可以工作

每个任务都需要一个volatile boolean和一个将其设置为true的方法