原文:http://java.dzone.com/articles/think-twice-using-java-8
作者:Lukas Krecan
翻译:长风
如果你去听Oracle的人谈论 Java 8 背后的设计抉择的话,你经常会听到他们说并行化是其主要的动机。并行化是lambdas、stream API及其它一些技术的背后驱动力。让我看一个stream API的例子:
private long countPrimes(int max) {
return range(1, max).parallel().filter(this::isPrime).count();
}
private boolean isPrime(long n) {
return n > 1 && rangeClosed(2, (long) sqrt(n)).noneMatch(divisor -> n % divisor == 0);
}
这里我们有一个方法 countPrimes,可以计算1到max之间有多少个素数。这个方法里面,首先通过range方法创建一个数字的 stream 对象,然后将这个 stream 转换成并行模式,接着那些不是素数的数字会被过滤掉,最后剩下的素数被计数。
你可以看到,stream API 允许我们以一种简洁和紧凑的方式去描述问题。而且,你只要调用一个 parallel() 方法就能实现并行化。并行化以后,stream 会被分成多个块,每块都会被独立处理,最后的结果又会被聚合在一起。我们上面那个 isPrime 方法的实现即低效,又是CPU密集型的,因此,我们可以利用并行化的优点,使用所有能用的 CPU core。
下面我们来看另一个例子:
private List<StockInfo> getStockInfo(Stream<String> symbols) {
return symbols.parallel()
.map(this::getStockInfo) //slow network operation
.collect(toList());
}
我们有一个股票代码的列表,我们必须调用一个很慢的网络操作去获取那些股票的明细。这不是一个CPU密集型操作,但是我们仍然可以利用并行。我们可以并行地去执行多个网络请求,这也是并行 stream 一个很好的应用,不是吗?
如果你在一个项目里面把上面两件事情都做了,那将会有一个大问题,你发现了吗?这个问题是因为所有的并行 stream 会使用一个共同的 fork-join 线程池,如果你提交了一个长时间运行的任务,那么就会阻塞线程池中所有的线程。结果就是你阻塞了其它所有使用并行 stream 的任务。想象一下,在一个 servlet 环境中,当一个请求调用 getStockInfo 方法,另一个调用 countPrimes 方法。那么其中一个请求将会阻塞另一个请求,尽管这两个请求要求的是不同的系统资源。更糟的是,你还不能给并行 stream 指定线程池,在一个 class loader 里面只能共用同一个线程池。
让我们用下面的例子来说明:
private void run() throws InterruptedException {
ExecutorService es = Executors.newCachedThreadPool();
// Simulating multiple threads in the system
// if one of them is executing a long-running task.
// Some of the other threads/tasks are waiting
// for it to finish
es.execute(() -> countPrimes(MAX, 1000)); //incorrect task
es.execute(() -> countPrimes(MAX, 0));
es.execute(() -> countPrimes(MAX, 0));
es.execute(() -> countPrimes(MAX, 0));
es.execute(() -> countPrimes(MAX, 0));
es.execute(() -> countPrimes(MAX, 0));
es.shutdown();
es.awaitTermination(60, TimeUnit.SECONDS);
}
private void countPrimes(int max, int delay) {
System.out.println(
range(1, max).parallel()
.filter(this::isPrime).peek(i -> sleep(delay)).count()
);
}
上面,我们模拟了6个线程,全都执行CPU密集型任务。第一个任务是“有问题”的,它每找到一个素数都会休眠一秒钟。当然,这只是我们假想出来的例子,你可以把它想像成一个被卡住或者是一个执行阻塞操作的线程。
问题是当这段代码执行时,会发生什么呢?我们有6个任务,其中一个要花一天才能执行完,而其它的很快就能执行完。毫无意外,每次执行的结果都不一样,有时候所有正常的任务都完成了,有时候则会有几个被卡在“有问题”的那个任务后面。你想你生成环境中的系统有这样的行为吗?一个有问题的任务撂倒整个应用的其它部分?我可不想要。
要确保不会发生这种事,有两个选择。一个是确保所有进入共同 fork-join 线程池的任务都不会卡住,能够在一个合理的时间内完成 。这个说起来容易做起来难,尤其是在一个复杂的应用系统中。另一个选择就是,在 Oracle 允许我们给一个并行 stream 指定线程池之前,不要使用并行 stream。
Resources:
Interview with Brian Goetz
A Java Parallel Calamity
Published at DZone with permission of its author, Lukas Krecan.
分享到:
相关推荐
Java 8 引入了流(Streams)的概念,它是一系列支持顺序和并行处理的元素,并且可以透明地以不同的方式处理,包括过滤、映射转换、查找、匹配和聚合。Java 8 Streams API 是 Java 新增的特性之一,提供了一个高级的...
Java 8是Java编程语言的一个重要版本,引入了许多新特性,极大地提升了开发效率和代码质量。这个"java8中文api"是一个中文版的Java 8 API文档,对于中国开发者来说,是一个非常有用的参考资料。API(Application ...
《高清彩版 Java 8 in Action: Lambdas, Streams and Functional-style Programming》这本书详细介绍了 Java 8 中的重要新特性,包括 Lambda 表达式、Stream API 以及函数式编程风格的应用。通过学习这些内容,...
7. **并发改进**:Java 8在并发处理方面也有所增强,例如`ForkJoinPool`和`Parallel Streams`,它们利用多核处理器的并行计算能力,加速了大量数据的处理速度。 8. ** Nashorn JavaScript引擎**:Java 8集成了...
2. 在 Java 中使用 Stream 有什么好处? 使用 Stream 可以声明式编程、并行执行,并且可以通过过滤、映射和归约等优化操作来处理大型数据集。 3. Stream 上的常见操作有哪些? Stream 上的常见操作包括 filter...
在本示例中,我们看到如何通过Java 8的并行流(Parallel Streams)和并行计算显著提高代码执行效率。这个案例展示了如何将一个原本复杂的并行计算框架精简为两个关键类,从而实现更简洁、高效的编程模型。 首先,...
以上只是Java 8中部分重要特性,实际的“Java+8实战”书籍可能会涵盖更多细节,如如何使用新的日期和时间API进行日期计算、如何利用流API进行复杂的数据处理,以及如何有效地使用lambda和函数式接口进行函数式编程。...
Get an easy introduction to reactive streams in Java to handle concurrency, data streams, and the propagation of change in today's applications. This compact book includes in-depth introductions to ...
4. **并行流(Parallel Streams)**:Java 8 支持并行流,可以在多核处理器上并行执行 Stream 操作,显著提高性能。默认情况下,`.parallel()` 方法会将流转换为并行流。 5. **收集器(Collectors)**:用于将 ...
5. **Parallel Streams**:Java 8引入了并行流,这是对集合操作的重大改进。通过`.parallelStream()`方法,可以将流操作转化为并行执行,充分利用多核处理器的优势。 6. **原子类**:`java.util.concurrent.atomic`...
"Java Lambdas and Parallel Streams" English | ISBN: 1484224868 | 2016 | 104 pages | PDF | 3 MB This compact book introduces the concepts of Java Lambdas and parallel Streams in a concise form. It ...
4. **方法引用来代替Lambda**:除了Lambda表达式,Java 8还允许使用方法引用。方法引用可以更直接地将已有方法与函数式接口关联,如`Arrays::sort`或`String::length`。 5. **Optional类**:为了解决null值带来的...
Java 8是Java编程语言的一个重大版本更新,它引入了许多创新特性和改进,极大地提升了开发效率和代码质量。这份“写给大忙人看的Java 8 中文 PDF 高清版”文档无疑是一个极好的资源,可以帮助Java开发者快速理解和...
此外,本书中使用了商标名、商标标志、服务标志以及类似术语,即使它们没有被明确标识为受专利权保护,也不应被视为对它们是否属于专利权的评价。 书籍中的内容是基于出版日期时的了解而提供的,作者、编辑和出版商...
Java 8 API是Java开发的重要参考资料,它包含了Java 8版本的所有公共类、接口和方法的详细说明。这个官方离线版的文档让开发者无需互联网连接也能查阅相关信息,极大地方便了学习和开发过程。 首先,Java 8引入了...
Reactive Streams in Java Concurrency with RxJava, Reactor, and Akka Streams. 2019年最新出版,清晰文字PDF,带目录书签。
Java 8允许在接口中使用default关键字来定义一个具有默认实现的方法,这被称为扩展方法。接口的实现类可以选择继承或重写这些方法。这使得在不破坏现有代码的情况下为接口添加新功能成为可能。 2. Lambda表达式...
Java Lambdas and Parallel Streams 英文无水印pdf pdf使用FoxitReader和PDF-XChangeViewer测试可以打开