浅讨Java 8的4个隐秘的错误


 1、不小心创建了一个”无限"流 

你可能一不留神就创建了一个无限流。就拿下面这个例子来说: 

Java代码  

1. IntStream.iterate(0, i -> i + 1)  

2.          .forEach(System.out::println);  


流的问题就在于它有可能是无限的,如果你的确是这样设计的话。唯一的问题就是,这并不是你真正想要的。因此,你得确保每次都给流提供一个适当的大小限制: 

Java代码  

1. // That's better  

2. IntStream.iterate(0, i -> i + 1)  

3.          .limit(10)  

4.          .forEach(System.out::println);  


2、 不小心创建了一个“隐藏的”无限流 


这个话题是说不完的。你可能一不小心就真的创建了一个无限流。比如说下面的这个: 

Java代码  

1. IntStream.iterate(0, i -> ( i + 1 ) % 2)  

2.          .distinct()  

3.          .limit(10)  

4.          .forEach(System.out::println);  




这样做的结果是: 

·  我们生成了0和1的交替数列 

·  然后只保留不同的数值,比如说,一个0和一个1 

·  然后再将流的大小限制为10 

·  最后对流进行消费 

好吧,这个distinct()操作它并不知道iterate()所调用的这个函数生成的只有两个不同的值。它觉得可能还会有别的值。因此它会不停地从流中消费新的值,而这个limit(10)永远也不会被调用到。不幸的是,你的应用程序会崩掉。 


3、 不小心创建了一个”隐藏”的并行无限流 

我还是想继续提醒下你,你可能真的一不小心就消费了一个无限流。假设你认为distinct()操作是会并行执行的。那你可能会这么写: 

Java代码  

1. IntStream.iterate(0, i -> ( i + 1 ) % 2)  

2.          .parallel()  

3.          .distinct()  

4.          .limit(10)  

5.          .forEach(System.out::println);  


现在我们可以知道的是,这段代码会一直执行下去。不过在前面那个例子中,你至少只消耗了机器上的一个CPU。而现在你可能会消耗四个,一个无限流的消费很可能就会消耗掉你整个系统的资源。这可相当不妙。这种情况下你可能得去重启服务器了。

4.并行流死锁 

如果你没有正确地进行同步的话,所有的并发系统都可能碰到死锁。现实中的例子可能不那么明显,不过如果你想自己创造一个场景的话倒是很容易。下面这个parallel()流肯定会造成死锁:Java代码  

1. Object[] locks = { new Object(), new Object() };  

2.   

3. IntStream  

4.     .range(1, 5)  

5.     .parallel()  

6.     .peek(Unchecked.intConsumer(i -> {  

7.         synchronized (locks[i % locks.length]) {  

8.             Thread.sleep(100);  

9.   

10.             synchronized (locks[(i + 1) % locks.length]) {  

11.                 Thread.sleep(50);  

12.             }  

13.         }  

14.     }))  

15.     .forEach(System.out::println);  

注意这里Unchecked.intConsumer()的使用,它把IntConsumer接口转化成了 org.jooq.lambda.fi.util.function.CheckedIntConsumer,这样你才可以抛出已检查异常。