下面的代码代表了我试图解决的问题的玩具示例。
想象一下,我们有一个原始数据流originalStream
,目标是应用2个非常不同的数据处理。作为一个例子,一个数据处理将每个元素乘以2并对结果求和(dataProcess1
),另一个将乘以4并求结果(dataProcess2
)。显然,现实生活中的操作不会那么简单....
想法是使用jOOλ
来复制流并将两个操作应用于2个流。但是,诀窍在于我想在不同的线程中运行两种数据处理。由于originalStream.duplicate()
不是开箱即用的线程安全,因此下面的代码将无法给出正确的结果: result1 = 570; result2 = 180 。相反,代码可能无法预测在NPE上失败,产生错误的结果或(有时)甚至给出正确的结果......
问题是如何最低限度地修改代码,使其成为线程安全的。
注意我不想先将流收集到列表中,然后生成2个新流。相反,我希望继续使用流,直到最终在数据流程结束时收集它们。它可能不是最有效也不是最合乎逻辑的事情,但我认为它在概念上很有趣。 请注意我希望继续使用org.jooq.lambda.Seq
( group:'org.jooq',名称:'jool',版本:'0.9.12')尽可能多,因为真正的数据处理函数将使用特定于此库的方法,而不会出现在常规Java流中。
Seq<Long> originalStream = seq(LongStream.range(0, 10));
Tuple2<Seq<Long>, Seq<Long>> duplicatedOriginalStream = originalStream.duplicate();
ExecutorService executor = Executors.newFixedThreadPool(2);
List<Future<Long>> res = executor.invokeAll(Arrays.asList(
() -> duplicatedOriginalStream.v1.map(x -> 2 * x).zipWithIndex().map(x -> x.v1 * x.v2).reduce((x, y) -> x + y).orElse(0L),
() -> duplicatedOriginalStream.v2.map(x -> 4 * x).reduce((x, y) -> x + y).orElse(0L)
));
executor.shutdown();
System.out.printf("result1 = %d\tresult2 = %d\n", res.get(0).get(), res.get(1).get());