在Data Geekery ,我们喜欢Java。 而且,由于我们真的很喜欢jOOQ的流畅的API和查询DSL ,我们对Java 8将为我们的生态系统带来什么感到非常兴奋。 我们已经写了一些关于Java 8好东西的博客 ,现在我们觉得是时候开始一个新的博客系列了……
Java 8星期五
每个星期五,我们都会向您展示一些不错的教程风格的Java 8新功能,这些功能利用了lambda表达式,扩展方法和其他好东西。 您可以在GitHub上找到源代码 。
Java 8 Goodie:精益并发
曾经有人说过(不幸的是,我们没有消息来源了):
初级程序员认为并发很难。
经验丰富的程序员认为并发很容易。
高级程序员认为并发很难。
没错 但好的一面是,Java 8至少将通过使使用lambda和许多改进的API编写并发代码变得更容易来改善性能。 让我们仔细看看:
Java 8在JDK 1.0 API上的改进
java.lang.Thread
从JDK 1.0开始就已经存在。 java.lang.Runnable
也是如此,它将在Java 8中用FunctionalInterface
进行注释。
从现在开始,如何最终将Runnable
提交到Thread
几乎是不费吹灰之力。 假设我们有一个长期运行的操作:
public static int longOperation() {
System.out.println("Running on thread #"
+ Thread.currentThread().getId());
// [...]
return 42;
}
然后,我们可以通过各种方式将此操作传递给Threads
,例如
Thread[] threads = {
// Pass a lambda to a thread
new Thread(() -> {
longOperation();
}),
// Pass a method reference to a thread
new Thread(ThreadGoodies::longOperation)
};
// Start all threads
Arrays.stream(threads).forEach(Thread::start);
// Join all threads
Arrays.stream(threads).forEach(t -> {
try { t.join(); }
catch (InterruptedException ignore) {}
});
正如我们在之前的博客文章中提到的那样,lambda表达式找不到解决受检异常的精益方法真是可惜。 java.util.function
包中新添加的功能接口均不允许抛出已检查的异常,从而将工作留给了调用站点。
因此,在上一篇文章中 ,我们发布了jOOλ(也称为jOOL,jOO-Lambda) ,它将每个JDK的功能接口包装在一个等效的功能接口中,该功能接口允许抛出检查异常。 这对于旧的JDK API(例如JDBC)或上述Thread API尤其有用。 使用jOOλ ,我们可以这样写:
// Join all threads
Arrays.stream(threads).forEach(Unchecked.consumer(
t -> t.join()
));
Java 8在Java 5 API上的改进
在Java 5出色的ExecutorService
发行之前,Java的多线程API一直处于休眠状态。 管理线程一直是一个负担,人们需要外部库或J2EE / JEE容器来管理线程池。 使用Java 5可以轻松得多。现在,我们可以将 Runnable
或Callable
提交给ExecutorService
,后者可以管理自己的线程池。
这是一个示例,说明如何在Java 8中利用这些Java 5并发API:
ExecutorService service = Executors
.newFixedThreadPool(5);
Future[] answers = {
service.submit(() -> longOperation()),
service.submit(ThreadGoodies::longOperation)
};
Arrays.stream(answers).forEach(Unchecked.consumer(
f -> System.out.println(f.get())
));
注意,我们如何再次使用jOOλ中的UncheckedConsumer
将从get()
调用引发的已检查异常包装在RuntimeException
。
Java 8中的并行和ForkJoinPool
现在,Java 8 Streams API在并发性和并行性方面发生了很多变化。 例如,在Java 8中,您可以编写以下代码:
Arrays.stream(new int[]{ 1, 2, 3, 4, 5, 6 })
.parallel()
.max()
.ifPresent(System.out::println);
尽管在这种特殊情况下没有必要,但仍然有趣的是,仅调用parallel()
即可在您的JDK内部ForkJoinPool的所有可用线程上运行IntStream.max() reduce操作,而无需担心涉及ForkJoinTasks
。 这可能非常有用,因为并非所有人都欢迎JDK 7 ForkJoin API引入的复杂性 。
在这篇有趣的InfoQ文章中了解有关Java 8并行流的更多信息。
有关Java 8的更多信息
并行是新Streams API背后的主要驱动力之一。 在许多情况下,仅能够在Stream上设置一个名为parallel()
的标志就非常了不起。
在上一个示例中,我们看到了OptionalInt.ifPresent()方法,该方法在先前的reduce操作成功的情况下执行IntConsumer参数。
诸如Scala之类的其他语言也知道一种“选项”类型以改善NULL处理。 我们之前已在博客上介绍了Optional ,我们将在Java 8 Streams的上下文中重申Java 8 Optional
类型,敬请期待!
同时,请查看Eugen Paraschiv出色的Java 8资源页面
翻译自: https://www.javacodegeeks.com/2014/03/java-8-friday-goodies-lean-concurrency.html