相对较新的published article about replacing executors by actors在 Java 8 中声明使用匿名内部类 Runnable
如下所示:
// Functional operation
executor.execute(new Runnable() {
System.out.println(data);
});
理想情况下可以替换为执行相同操作的对垃圾收集器更友好的技术:
Actor<String> actor = new Actor<>(parentExecutor, ::onMessage);
// Equivalent functional operation
actor.act("Hello world");
public void onMessage(String message) {
System.out.println(message);
}
这是有道理的 one should generally use method references over anonymous inner classes (即新的Runnable
,新的Callable
)。这个想法已经存在了一段时间了。使用此 Actor 模式似乎还有另一个更有效的微妙之处,但文章中没有明确解释。
我理解使用:
executor.execute(() -> System.out.println(data));
显然效率低下(尽管稍微低效),因为编译器必须创建一个生成的方法invokedynamic
“调用站点”并在调用时引用它。
这里的优点是(1)直接方法引用没有invokedynamic
方法生成的开销,并且(2)还可以允许您传递方法参数直接引用这个?相对于我们已经使用和了解的模式,本文中提出的这个想法的关键要点是什么?
最佳答案
本文并没有在任何地方说使用 lambda 表达式而不是方法引用会更糟糕。
关键思想是,而不是调用
executor.execute(new Runnable() {
public void run() {
System.out.println(data);
}
});
数百万次,创建数百万个 Runnable
实例,您这样做
Actor<String> actor = new Actor<>(parentExecutor, this::onMessage);
一次并调用
actor.act(data);
数百万次。 Actor
在内部使用字符串队列(或您使用的任何数据项),而不是将任何项包装到另一个对象中,并使用单个 Runnable
进行排队。
这样做时也同样有效
Actor<String> actor = new Actor<>(parentExecutor, x -> System.out.println(x));
相反甚至
Actor<String> actor = new Actor<>(parentExecutor, new ActorListener<String>() {
public void onMessage(String message) {
System.out.println(message);
}
});
由于该代码仅执行一次,因此其技术细节无关紧要。
Lambda 表达式或方法引用使使用此模式变得更容易,但不是此模式的基石。而且这篇文章并没有说别的。它所说的只是“使用 lambda 表达式会变得非常优雅”
<小时/>1 挑剔的是,它在幕后使用了一个 ConcurrentLinkedQueue
,它将每个项目包装到一个节点对象中,这表明临时对象可能是多么不相关。使用基于数组的队列会更加一致,但是队列不可能是非阻塞的。鱼与熊掌不可兼得……
关于java - 可运行对象与方法引用和垃圾,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47445188/