Java 8并行流findFirst

Java 8并行流findFirst,java,parallel-processing,java-8,Java,Parallel Processing,Java 8,假设我们有一个这样的工人列表: List<Worker> workers = new ArrayList<>(); workers.add(new Worker(1)); workers.add(new Worker(2)); workers.add(new Worker(3)); workers.add(new Worker(4)); workers.add(new Worker(5)); 但是有一个问题,我不想等待所有工人完成他们的工作,然后再找到第一个工人,而是第

假设我们有一个这样的工人列表:

List<Worker> workers = new ArrayList<>();
workers.add(new Worker(1));
workers.add(new Worker(2));
workers.add(new Worker(3));
workers.add(new Worker(4));
workers.add(new Worker(5));
但是有一个问题,我不想等待所有工人完成他们的工作,然后再找到第一个工人,而是第一个工人在他完成工作后就找到了

public class Test {

    public static void main(String[] args) {
        List<Worker> workers = new ArrayList<>();
        workers.add(new Worker(1));
        workers.add(new Worker(2));
        workers.add(new Worker(3));
        workers.add(new Worker(4));
        workers.add(new Worker(5));
        Worker first = workers.parallelStream().filter(Worker::finish).findFirst().orElse(null);
        if (first != null) {
            System.out.println("id : " + first.id);
        }
    }

    static class Worker {

        int id;

        Worker(int id) {
            this.id = id;
        }

        boolean finish() {
            int t = id * 1000;
            System.out.println(id + " -> " + t);
            try {
                Thread.sleep(t);
            } catch (InterruptedException ignored) {
            }
            return true;
        }

    }

}
公共类测试{
公共静态void main(字符串[]args){
List workers=new ArrayList();
增加(新工人(1));
增加(新工人(2));
增加(新工人(3));
增加(新工人(4));
增加(新工人(5));
Worker first=workers.parallelStream().filter(Worker::finish).findFirst().orElse(null);
如果(第一个!=null){
System.out.println(“id:+first.id”);
}
}
静态类工作者{
int-id;
工作人员(内部id){
this.id=id;
}
布尔完成(){
int t=id*1000;
系统输出打印项次(id+“->”+t);
试一试{
睡眠(t);
}捕获(InterruptedException被忽略){
}
返回true;
}
}
}
使用
java.util.Stream
有什么方法可以实现它吗


谢谢。

当您使用
finish
方法作为流的筛选器时,这意味着为了评估特定工作进程的筛选器谓词,工作进程必须完成其工作

但是,当您以并行流的形式运行此代码时,可能会同时对多个worker应用过滤器,在这种情况下,第一个完成的将为您提供输出。但是,您无法控制并行流将使用多少线程。它可能决定一些辅助线程应该在同一线程上处理,在这种情况下,其中一些根本不会被处理(因为终端操作要求只有一个辅助线程完成其处理)


因此,如果您的目标是同时为所有工作人员执行
finish
,则不能使用流(甚至不能使用并行流)。

您似乎对
流有严重的误解<代码>流
s不用于启动工作程序。事实上,如果您使用
findFirst
,则可能只启动第一个工作进程,而不启动任何工作进程。因此,它也不会等待“所有工作线程完成”,而只等待当前挂起的线程。但是,由于您有一个相当小的流,因此可能所有工作线程都已启动,因为您的环境中有尽可能多的可用线程。但这并不是一个可以保证的行为


请注意,如果使用顺序流而不是并行流,它肯定只处理第一项(因为它返回
true
),而不处理其他项。但是,由于流实现无法预测结果,它将尊重您通过并行执行“加速”操作的请求,并可能开始使用更多线程提前处理更多项。

您可以尝试使用Java反应式扩展中的
可观察的
RxJava
)。下面的示例代码

public class Example {
  public static void main(String[] args) {
    Maybe<Worker> workerResult = Observable.fromArray(Worker.run(1), Worker.run(2), Worker.run(3), Worker.run(4), Worker.run(5))
        .flatMap(worker -> (Observable<Worker>) worker)
        .firstElement();
    workerResult.subscribe(onNext -> System.out.println("First worker [" + onNext.toString() + "]"));

    try {
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

class Worker {
  private int id;
  static Observable run(int id) { return Observable.just(new Worker(id)).observeOn(Schedulers.computation()).doOnNext(Worker::process); }
  private Worker(int id) { this.id = id; }
  public void process() {
    try {
      Thread.sleep(new Random().nextInt(2000));
    } catch (InterruptedException e) {
      System.out.println(String.format("[%s] Thread interrupted [%s]", Thread.currentThread(), id));
    }
    System.out.println(String.format("[%s] Worker [%s]", Thread.currentThread(), id));
  }
    public String toString() { return "Worker [" + id + "]"; }
}

我知道这是个老问题,但在这里找到了一些很好的解决方案:

在InvokeAny下:

批量提交可调用项的另一种方法是invokeAny()方法 它的工作原理与invokeAll()略有不同。而不是返回 未来对象此方法阻止,直到第一个可调用对象终止 并返回该可调用函数的结果


将stream更改为可调用的集合。它看起来非常干净。

CompletionExecutorService可能更适合您的用例。即使有办法做到这一点,也可能是错误的方法。stream不是任务调度框架,而是ExecutorService。使用正确的工具,它会变得更简单!实际上,因为
ArrayList提供了一个有序的流,并且使用了
findFirst
而不是
findAny
,结果将是第一个worker实例(返回
true
)。在这里使用
parallel
意味着增加了开销。
public class Example {
  public static void main(String[] args) {
    Maybe<Worker> workerResult = Observable.fromArray(Worker.run(1), Worker.run(2), Worker.run(3), Worker.run(4), Worker.run(5))
        .flatMap(worker -> (Observable<Worker>) worker)
        .firstElement();
    workerResult.subscribe(onNext -> System.out.println("First worker [" + onNext.toString() + "]"));

    try {
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

class Worker {
  private int id;
  static Observable run(int id) { return Observable.just(new Worker(id)).observeOn(Schedulers.computation()).doOnNext(Worker::process); }
  private Worker(int id) { this.id = id; }
  public void process() {
    try {
      Thread.sleep(new Random().nextInt(2000));
    } catch (InterruptedException e) {
      System.out.println(String.format("[%s] Thread interrupted [%s]", Thread.currentThread(), id));
    }
    System.out.println(String.format("[%s] Worker [%s]", Thread.currentThread(), id));
  }
    public String toString() { return "Worker [" + id + "]"; }
}
[Thread[RxComputationThreadPool-2,5,main]] Worker [2]
[Thread[RxComputationThreadPool-1,5,main]] Thread interrupted [1]
[Thread[RxComputationThreadPool-1,5,main]] Worker [1]
[Thread[RxComputationThreadPool-4,5,main]] Thread interrupted [4]
[Thread[RxComputationThreadPool-3,5,main]] Thread interrupted [3]
[Thread[RxComputationThreadPool-3,5,main]] Worker [3]
[Thread[RxComputationThreadPool-4,5,main]] Worker [4]
First worker [Worker [2]]