如何在Java流的单个步骤上使用并行执行

如何在Java流的单个步骤上使用并行执行,java,java-stream,Java,Java Stream,我在我的流操作中执行一个昂贵的操作,我希望多线程,但其余的操作应该是单线程的。例如: package test; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.IntStream; public class TestStreams { private static Set<String> expensiveThreads = Conc

我在我的流操作中执行一个昂贵的操作,我希望多线程,但其余的操作应该是单线程的。例如:

package test;

import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.IntStream;

public class TestStreams {
    private static Set<String> expensiveThreads = ConcurrentHashMap.newKeySet();
    private static Set<String> cheapThreads = ConcurrentHashMap.newKeySet();

    public static void main(String[] args) {
        IntStream.range(1, 1000).parallel().map(i -> myExpensiveMap(i))
                .unparallel()  //does not compile
                .forEach(i -> myCheapOperation(i));
        System.out.println("Expensive Threads:" + expensiveThreads);
        System.out.println("Cheap Threads:    " + cheapThreads);
    }

    private static void myCheapOperation(int i) {
        cheapThreads.add(Thread.currentThread().getName());
    }

    private static int myExpensiveMap(int i) {
        expensiveThreads.add(Thread.currentThread().getName());
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return i;
    }
}
StreamSupport.stream(
                IntStream.range(1, 1000).parallel().map(i -> myExpensiveMap(i))
                        .spliterator(), false)
                .forEach(i -> myCheapOperation(i));

Expensive Threads:[main]
Cheap Threads:    [main]
StreamSupport.stream(
                IntStream.range(1, 1000).parallel().map(i -> myExpensiveMap(i))
                        .spliterator(), true)
                .forEach(i -> myCheapOperation(i));

Expensive Threads:[ForkJoinPool.commonPool-worker-1, ForkJoinPool.commonPool-worker-2, main, ForkJoinPool.commonPool-worker-3]
Cheap Threads:    [ForkJoinPool.commonPool-worker-1, ForkJoinPool.commonPool-worker-2, main, ForkJoinPool.commonPool-worker-3]
但我想要的结果是:

Expensive Threads:[ForkJoinPool.commonPool-worker-1, ForkJoinPool.commonPool-worker-2, main, ForkJoinPool.commonPool-worker-3]
Cheap Threads:    [main]
我曾尝试使用StreamSupport(Spliterator,false)包装原始流,但这将原始流限制为单线程处理。例如:

package test;

import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.IntStream;

public class TestStreams {
    private static Set<String> expensiveThreads = ConcurrentHashMap.newKeySet();
    private static Set<String> cheapThreads = ConcurrentHashMap.newKeySet();

    public static void main(String[] args) {
        IntStream.range(1, 1000).parallel().map(i -> myExpensiveMap(i))
                .unparallel()  //does not compile
                .forEach(i -> myCheapOperation(i));
        System.out.println("Expensive Threads:" + expensiveThreads);
        System.out.println("Cheap Threads:    " + cheapThreads);
    }

    private static void myCheapOperation(int i) {
        cheapThreads.add(Thread.currentThread().getName());
    }

    private static int myExpensiveMap(int i) {
        expensiveThreads.add(Thread.currentThread().getName());
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return i;
    }
}
StreamSupport.stream(
                IntStream.range(1, 1000).parallel().map(i -> myExpensiveMap(i))
                        .spliterator(), false)
                .forEach(i -> myCheapOperation(i));

Expensive Threads:[main]
Cheap Threads:    [main]
StreamSupport.stream(
                IntStream.range(1, 1000).parallel().map(i -> myExpensiveMap(i))
                        .spliterator(), true)
                .forEach(i -> myCheapOperation(i));

Expensive Threads:[ForkJoinPool.commonPool-worker-1, ForkJoinPool.commonPool-worker-2, main, ForkJoinPool.commonPool-worker-3]
Cheap Threads:    [ForkJoinPool.commonPool-worker-1, ForkJoinPool.commonPool-worker-2, main, ForkJoinPool.commonPool-worker-3]
或者使用parallel=true,例如:

package test;

import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.IntStream;

public class TestStreams {
    private static Set<String> expensiveThreads = ConcurrentHashMap.newKeySet();
    private static Set<String> cheapThreads = ConcurrentHashMap.newKeySet();

    public static void main(String[] args) {
        IntStream.range(1, 1000).parallel().map(i -> myExpensiveMap(i))
                .unparallel()  //does not compile
                .forEach(i -> myCheapOperation(i));
        System.out.println("Expensive Threads:" + expensiveThreads);
        System.out.println("Cheap Threads:    " + cheapThreads);
    }

    private static void myCheapOperation(int i) {
        cheapThreads.add(Thread.currentThread().getName());
    }

    private static int myExpensiveMap(int i) {
        expensiveThreads.add(Thread.currentThread().getName());
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return i;
    }
}
StreamSupport.stream(
                IntStream.range(1, 1000).parallel().map(i -> myExpensiveMap(i))
                        .spliterator(), false)
                .forEach(i -> myCheapOperation(i));

Expensive Threads:[main]
Cheap Threads:    [main]
StreamSupport.stream(
                IntStream.range(1, 1000).parallel().map(i -> myExpensiveMap(i))
                        .spliterator(), true)
                .forEach(i -> myCheapOperation(i));

Expensive Threads:[ForkJoinPool.commonPool-worker-1, ForkJoinPool.commonPool-worker-2, main, ForkJoinPool.commonPool-worker-3]
Cheap Threads:    [ForkJoinPool.commonPool-worker-1, ForkJoinPool.commonPool-worker-2, main, ForkJoinPool.commonPool-worker-3]

如何取消此流的并行?

.parallel
相反的是:


我不知道sequential,但是sequential()调用似乎覆盖了parallel()调用<代码>昂贵线程:[main]
廉价线程:[main]
除了在第一次操作后将其转储到集合中,然后将其流式传输到第二次操作中之外,可能没有其他选择。@Louis Wasserman:有。另一种方法是让第二个操作并行运行。便宜并不妨碍并行执行。流不是这样工作的。他们不需要这样做。如果
myCheapOperation
是一个便宜的操作,那么并行执行它应该没有问题。这确实会对封装产生负面影响。我希望能够从我的库中返回流,但是我不能阻止最终程序员以不受支持的方式使用它。例如,我必须考虑线程安全性,以防最终程序员碰巧使用<代码>并行()/代码>,并且在它们使用或不使用前述命令时,我必须考虑顺序。没有办法包装流,这样链式指令只会影响下游吗?根本不需要考虑。传递给
map
的函数应该始终是无干扰和无状态的,如果它满足了该契约,就不必关心线程安全。你永远不必关心订单。或者,你的流有定义的遭遇顺序,或者没有。如果有,溪流会小心的。