我正在创建一个链接CompleteableFuture
操作的演示示例,我认为我很接近,但有些东西我不知道。除了main()
中的最终“并行构建蛋糕”条款外,所有内容都会编译:
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.stream.*;
import java.time.*;
// Use decorator pattern to build up the cake:
interface Cake_ {
String describe();
}
class Cake implements Cake_ {
private int id;
public Cake(int id) { this.id = id; }
@Override
public String describe() {
return "Cake " + id;
}
}
abstract class Decorator implements Cake_ {
protected Cake_ cake;
public Decorator(Cake_ cake) {
this.cake = cake;
}
@Override
public String describe() {
return cake.describe();
}
@Override
public String toString() {
return describe();
}
}
class Frosted extends Decorator {
public Frosted(Cake_ cake) {
super(cake);
}
@Override
public String describe() {
return cake.describe() + " Frosted";
}
}
class Decorated extends Decorator {
public Decorated(Cake_ cake) {
super(cake);
}
@Override
public String describe() {
return cake.describe() + " Decorated";
}
}
// For the cake-building assembly line:
class CreateCakes implements Supplier<Cake> {
private int id;
public CreateCakes(int id) {
this.id = id;
}
@Override
public Cake get() {
return new Cake(id);
}
}
class FrostCakes implements Function<Cake, Frosted> {
@Override
public Frosted apply(Cake cake) {
return new Frosted(cake);
}
}
class DecorateCakes implements Consumer<Frosted> {
public Decorated result;
@Override
public void accept(Frosted fc) {
result = new Decorated(fc);
}
}
public class Test {
public static int NUM_OF_CAKES = 20;
public static void main(String[] args) {
// Change from the default number of threads:
System.setProperty(
"java.util.concurrent.ForkJoinPool" +
".common.parallelism", "" + NUM_OF_CAKES);
// Test/demonstrate the decorator pattern:
List<Cake_> decorated =
IntStream.range(0, NUM_OF_CAKES)
.mapToObj(Cake::new)
.map(Frosted::new)
.map(Decorated::new)
.collect(Collectors.toList());
decorated.forEach(System.out::println);
// Build cakes in parallel:
List<CompletableFuture<?>> futures =
IntStream.range(0, NUM_OF_CAKES)
.mapToObj(id -> new CreateCakes(id))
.map(CompletableFuture::supplyAsync)
.thenApply(new FrostCakes())
.thenAccept(new DecorateCakes())
.collect(Collectors.toList());
futures.forEach(CompletableFuture::join);
}
}
我意识到我在futures
列表的定义中缺少一些基本的理解,但我已经将它包含在内,以显示我尝试在这里完成的任务:一个蛋糕工厂与蛋糕创建过程的一部分并行运行。
答案 0 :(得分:3)
对您的问题的快速回答是thenApply
行无法编译,因为上面一行(map(CompletableFuture::supplyAsync)
)的结果返回Stream<CompletableFuture<Cake>>
而不是CompletableFuture<Cake>
。
您需要执行map(cakeFuture -> cakeFuture.thenApply(new FrostCakes()))
之类的操作。
但我认为需要做出更重要的一点。
如果您的示例仅用于教育目的,我建议您再做一两天的准备工作,更具体地说是阅读有关流操作的基础知识和CompletableFuture
。
通过这种方式,当您展示材料时,您会感到更自信,但更重要的是,您不会提供不完美的代码示例,这些示例可能会损害您的同事/学生关于如何流和{ {1}} s(甚至装饰器)。
我会指出一些我认为需要在你的例子中重做的事情。
CompletableFuture
的并行度级别并不总是一个好主意。默认情况下,它使用ForkJoinPool
返回的处理器数量,这是一个非常好的默认值。您需要有一个很好的理由将其更改为更多内容,因为在大多数情况下,您只会在调度和维护冗余线程时引入不必要的开销。将其更改为您计划触发的任务数量几乎总是一个坏主意(解释省略)。Runtime.availableProcessors()
,可以在不收集列表的情况下重写它们,并且可以说这将更好地演示使用Java 8流的流畅编程模型。forEach
之后添加.parallel()
来轻松解决此问题,但除非您从上面删除要列出的冗余集合,否则您将无法通过打印看到您已完成并行操作列表内容为IntStream.range()
。forEach
接口的类不是非常惯用的。我认为应该用相应的lambda表达式替换它们,即使在你的情况下这可能会导致lambda中有一个满口的lambda,除非你的第二个例子稍微被重写。java.util.function
会返回CompletableFuture.thenAccept
,因此在流处理的这个阶段,您将失去对已创建,磨砂和装饰蛋糕的引用。如果您不关心它们,或者如果您在装饰逻辑中记录了一些关于它们的内容,这可能没问题,但您的示例很容易误导人们最终收集的CompletableFuture<Void>
列表可用于覆盖蛋糕(毕竟,谁也不想吃蛋糕而且也吃它)。所以你的第一个例子看起来像
CompletableFuture
请注意由于并行执行而未对蛋糕ID进行排序。