強大的Stream並行流
一 瞭解Stream
Stream API(java.util.stream.*) Stream 是JAVA8中處理集合的關鍵抽象概念,它可以指定你希望對集合進行的操作,可以執行非常複雜的查詢、過濾和對映資料等操作。使用Stream API對集合資料進行操作,就類似於使用SQL執行資料查詢一樣。也可使用StreamAPI做並行操作,總之,StreamAPI提供了一種高效且易於使用的處理資料的方式。
Stream是資料渠道,用於運算元據源(集合、陣列等)所生成的元素序列。集合講的是資料,流講的是計算。
注意:
1 . Stream自己不會儲存元素。
2 . Stream不會改變源物件。相反,他們會返回一個持有結果的新Stream
3 . Stream操作是延遲執行的。這意味著他們會等到需要結果的時候才執行。
二 並行流與序列流
並行流就是把一個內容分成多個資料塊,並用不同的執行緒分成多個資料塊,並用不同的執行緒分別處理每個資料塊的流。
JAVA8 中將並行進行了優化,我們可以很容易的對資料進行並行操作。Stream API 可以宣告性地通過parallel() 與sequential() 在並行流與順序流之間進行切換。其實JAVA8底層是使用JAVA7新加入的Fork/Join框架:
Fork/Join框架與傳統執行緒池的區別:
採用“工作竊取”模式(work-stealing):當執行新的任務時它可以將其拆分分成更小的任務執行,並將小任務加到執行緒佇列中,然後再從一個隨機執行緒的佇列中偷一個並把它放在自己的佇列中。相對於一般的執行緒池實現,fork/join框架的優勢體現在對其中包含的任務的處理方式上.在一般的執行緒池中,如果一個執行緒正在執行的任務由於某些原因無法繼續執行,那麼該執行緒會處於等待狀態.而在fork/join框架實現中,如果某個子問題由於等待另外一個子問題的完成而無法繼續執行.那麼處理該子問題的執行緒會主動尋找其他尚未執行的子問題來執行.這種方式減少了執行緒的等待時間,提高了效能.
三 並行流與其他方式對比
下面就以一個求 0到一億、十億、五百億 的和來比較執行的效率:
1) 使用For迴圈求和
@Test
public void testFor() {
Instant start = Instant.now();
long sum = 0;
for (long i = 0; i <= 50000000000L; i++) {
sum += i;
}
System.out.println(sum);
Instant end = Instant.now();
System.out.println("五百億求和花費的時間為: " + Duration.between(start, end).toMillis());
}
執行結果:
5000000050000000
一億求和花費的時間為: 91
500000000500000000
十億求和花費的時間為: 868
-4378596987249509888
五百億求和花費的時間為: 33910
2) 使用Fork/Join框架求和
package com.lxj.java8;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
public class ForkJoinSum extends RecursiveTask<Long>{
private static final long serialVersionUID = 6011408981548802596L;
private long start;
private long end;
//臨界值
private final long THRESHHOLD = 10000L;
public ForkJoinSum() {
}
public ForkJoinSum(long start, long end) {
super();
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if(end - start <= THRESHHOLD) {
long sum = 0L;
for (long i = start; i <= end; i++) {
sum += i;
}
return sum;
}else {
long mid = (start + end)/2;
ForkJoinSum left = new ForkJoinSum(start,mid);
left.fork(); //分支
ForkJoinSum right = new ForkJoinSum(mid+1,end);
right.fork(); //分支
return left.join() + right.join(); //合併
}
}
public static void main(String[] args) {
Instant start = Instant.now(); //100000000L 1000000000L 50000000000L
ForkJoinTask<Long> forkJoinTask = new ForkJoinSum(0L,50000000000L);
ForkJoinPool forkJoinPool = new ForkJoinPool();
Long t = forkJoinPool.invoke(forkJoinTask);
System.out.println(t);
Instant end = Instant.now();
System.out.println("十億求和耗費的時間為: " + Duration.between(start, end).toMillis());
}
}
輸出結果:
5000000050000000
一億求和耗費的時間為: 106
500000000500000000
十億求和耗費的時間為: 508
-4378596987249509888
五百億求和耗費的時間為: 20256
3) 使用自己寫的遞迴求和(絕對是個人為了好玩才測的,就怕棧被資料撐爆了,要是C語言的話估計早遞迴爆記憶體了)
package com.lxj.java8;
import java.time.Duration;
import java.time.Instant;
public class Recursion {
private long start;
private long end;
private final long THRESHHOLD = 10000L;
public Recursion(long start, long end) {
super();
this.start = start;
this.end = end;
}
public Long getValue() {
long t = (start + end)/2;
if(end - start <= THRESHHOLD) {
long sum = 0L;
for(long i = start ; i <= end ; i++) {
sum += i;
}
return sum;
}else {
Recursion left = new Recursion(start, t);
Recursion right = new Recursion(t+1, end);
return left.getValue() + right.getValue();
}
}
//100000000L 1000000000L 50000000000L
/*
5000000050000000
一億求和耗費的時間為: 182
500000000500000000
十億求和耗費的時間為: 979
-4378596987249509888
五百億求和耗費的時間為: 114102
*/
public static void main(String[] args) {
Instant start = Instant.now();
Recursion binaryValue = new Recursion(0L, 50000000000L);
Long value = binaryValue.getValue();
System.out.println(value);
Instant end = Instant.now();
System.out.println("五百億求和耗費的時間為: "+Duration.between(start, end).toMillis());
}
}
執行結果:
5000000050000000
一億求和耗費的時間為: 182
500000000500000000
十億求和耗費的時間為: 979
-4378596987249509888
五百億求和耗費的時間為: 114102
4) 使用StreamAPI
@Test
public void testStream() {
Instant start = Instant.now();
//使用StreamAPI
OptionalLong result = LongStream.rangeClosed(0L, 50000000000L)
.parallel()
.reduce(Long::sum);
System.out.println(result.getAsLong());
Instant end = Instant.now();
System.out.println("五百億求和耗費的時間為: " + Duration.between(start, end).toMillis());
}
執行結果:
5000000050000000
一億求和耗費的時間為: 112
500000000500000000
十億求和耗費的時間為: 854
-4378596987249509888
五百億求和耗費的時間為: 20250
通過對比,Stream和Fork/Join框架在大資料的時候速度還是挺快的,For迴圈在資料小的時候是最快的。
相關文章
- Stream並行流詳解並行
- Java 8 Stream並行流Java並行
- Java 8 的Stream流那麼強大,你知道它的原理嗎?Java
- Stream流
- Java9系列第6篇-Stream流API的增強JavaAPI
- node中的流(stream)
- Stream流求和
- Java Stream 流如何進行合併操作Java
- Java8之Stream-強大的collect操作Java
- 流(stream):可讀流篇
- Java 8並行流的效能陷阱Java並行
- JDK8中的並行流JDK並行
- java的Stream流學習Java
- Java8的新特性--並行流與序列流Java並行
- stream流各種
- java-Stream流Java
- Stream 流模組
- Java Stream流使用Java
- 淺析nodejs中的stream(流)NodeJS
- 瞭解nodeJs中的流(stream)NodeJS
- node.js中的流(stream)Node.js
- java stream()流對兩個集合進行比對Java
- node中的流的介紹(Stream)
- 此流非彼流——Stream詳解
- JDK新特性--Stream流JDK
- [Java基礎]Stream流Java
- Java8——Stream流Java
- java 8 特性——stream流Java
- 【重學Java】Stream流Java
- Nodejs教程24:Stream流NodeJS
- Node.js Stream(流)Node.js
- Node.js Stream(流)Node.js
- Nodejs 實踐 -- Stream 流NodeJS
- Java中的函數語言程式設計(八)流Stream並行程式設計Java函數程式設計並行行程
- Java8 Stream流的合併Java
- RabbitMQ推出類似Kafka的流StreamMQKafka
- NODE Stream流總結(1)
- NODE Stream流總結(2)