序列化CompletableFuture字节码

时间:2015-10-11 19:40:09

标签: java serialization lambda java-8 completable-future

我想创建一个CompletableFuture链,将其序列化为磁盘作为字节码/参数,并在将来的某个时刻再次运行它。例如:

CompletableFuture<String> chain = new CompletableFuture<String>();

CompletableFuture<String> pos = chain;
for (int ii = 0; ii < 4; ++ii) {
    final int val = ii;
    pos = pos.thenApply( (s)->{ return s+" plus"+val;} );
}
pos = pos.thenApply( (s)->{ return "The final answer is "+s;} );

saveDisk( "chaindata.dat", chain, pos );
/////
// at some time in the future, perhaps in another program
Pair<CompletableFuture<String>,CompletableFuture<String>> newChain = loadFrom( "chaindata.dat" );
newChain.getA().complete("Hello");
String result = newChain.getB().get(); // should get hello with all post processing
// result==The final answer is Hello plus0 plus1 plus2 plus3

这当然很棘手,即使字节码存储在当前的类文件中(可以用Class.getResource()检索),我也需要在链中设置的最终变量也是持久的。 /> (参见final int var = ii;上面的for循环)

我未来的目标实际上是在具有大多数相同类文件的从属进程上运行链(但不是创建链的类文件) 我会写字节码/数据,在从属进程上重新加载它,并运行它。

如上所述,我相信在实例化时在循环中创建的最终变量是其中棘手的部分。即。 pos = pos.thenApply( (s)->{ return s+" plus"+ val ;} );

有没有人建议如何做到这一点?

1 个答案:

答案 0 :(得分:0)

首先发出警告,来自the Lambda Expressions tutorial

  

如果lambda表达式的目标类型和捕获的参数是可序列化的,则可以serialize。但是,与inner classes一样,强烈建议不要 lambda表达式的序列化

(强调是我的)

此要求的问题是CompletableFuture不是Serializable,并且将其扩展为CompletableFuture要求您从thenApply()复制很多代码–因为您需要覆盖许多方法(例如CompletableFuture)使它们返回新的可序列化类型。

但是,可以做的是序列化要执行的操作链。由于基本上是一个将SerializableFunction转换为另一个函数,因此最简单的方法是定义一种private interface SerializableFunction<T, R> extends Serializable { R apply(T t); default <V> SerializableFunction<T, V> andThen(SerializableFunction<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } } 类型:

Function

注意:我明确选择不扩展andThen(),因为andThen()参数不相同(因此它不会被覆盖),并且您需要使用CompletableFuture参数可以序列化,以便返回的lambda也可以序列化(按照上面的警告)。

然后您可以通过将所有private static SerializableFunction<CompletableFuture<String>, CompletableFuture<String>> createChain() { SerializableFunction<CompletableFuture<String>, CompletableFuture<String>> result = c -> c; for (int ii = 0; ii < 4; ++ii) { final int val = ii; result = result.andThen(pos -> pos.thenApply(s -> s + " plus" + val)); } return result.andThen(pos -> pos.thenApply(s -> "The final answer is " + s)); } 调用包装在lambda表达式中来创建链:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(createChain());
oos.close();

ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
SerializableFunction<CompletableFuture<String>, CompletableFuture<String>> chain =
        (SerializableFunction<CompletableFuture<String>, CompletableFuture<String>>) ois.readObject();

CompletableFuture<String> future = new CompletableFuture<>();
CompletableFuture<String> newChain = chain.apply(future);
newChain.thenAccept(System.out::println);
future.complete("Hello");

并根据需要对其进行序列化:

Math.pow

输出:

  

最终答案是Hello +0 plus1 plus2 plus3

请注意,要使其正常工作,捕获的变量确实已被序列化(由SerilizedLambda处理),但是反序列化的JVM仍然需要兼容版本的lambda代码本身。

考虑到这一点,最好在专用结构中表示您的运营链,然后处理该结构以远程重建链。