填坑
先來填坑,不知你們還記得我在第五章《串行與並行》中留的坑嗎?下面咱們就來繼續挖它,經過剖析源碼,一層一層撥開它的心。函數
萬流之眼 StreamSupport輔助類lua
爲何只是將parallel標誌位設爲false或true就能夠關閉或開啓並行,真正的實現原理是什麼呢?咱們先來看看集合類的stream方法與parallelStream方法有什麼不一樣,結果可能使人啞然失笑。code
//Collection接口中的默認實現
default Stream<E> stream() {對象
return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {繼承
return StreamSupport.stream(spliterator(), true);
}
這兩個方法都是像咱們以前使用Spliterator的時候那樣,直接調用了StreamSupport的stream方法,區別只在於最後的並行標誌位。相似的代碼也出如今了Stream類的of、iterate、generate等方法中,由此能夠推斷,StreamSupport的stream方法就是是系統類庫中產生流的惟一方法,固然這裏說的流不包括基本類型流。接口
追本溯源 Pipeline世家ip
StreamSupport的stream方法返回了一個ReferencePipeline.Head對象。ReferencePipeline是個抽象類,繼承了AbstractPipeline,並實現了其中大多數的方法,而Head是ReferencePipeline的內部類,繼承了ReferencePipeline並實現了剩下的兩個方法。總結一下就是Head內部類繼承了ReferencePipeline抽象類繼承了AbstractPipeline抽象類實現了Stream接口,因此Head就是咱們最後獲得的Stream。源碼
最終實現 Head內部類it
Head中對ReferencePipeline留下的兩個沒有實現的方法的實現看起來很古怪,都是直接拋出了UnsupportedOperationException,表示不支持該操做。咱們也不用關注這些,而是把注意力放在它重寫的另外兩個方法上,名字你們都很熟悉,forEach和forEachOrdered。這兩個方法的重寫也很簡單,以forEach爲例:io
if (!isParallel()) { sourceStageSpliterator().forEachRemaining(action); } else { super.forEach(action); }
該方法僅僅只是判斷一下isParallel,若是是並行的就仍是調父類的方法,不然就調用Spliterator的forEachRemaining方法,該方法在Spliterator接口中有默認的實現,只有一行代碼:
do { } while (tryAdvance(action));
是否是很絕,什麼也不作,等到tryAdvance返回false就結束。tryAdvance咱們前面已經講到,是用來判斷分割式迭代是否結束的。也就是說,若是流是串行的,就不會進行分割,這時候Spliterator就變成了普通的迭代器,從頭至尾逐個進行操做。
命令執行 TerminalOp衆子
對於並行流,Head則會調用父類ReferencePipeline中的同名方法,該方法中又會調用evaluate方法,根據不一樣的行爲如forEach、reduce來選擇執行不一樣的操做。全部的操做類都要實現TerminalOp接口,如forEach操做就對應着ForEachOp類,reduce操做對應着ReduceOp類,這些操做會做爲參數傳給evaluate方法。evaluate方法最後仍是會根據isParallel來選擇執行並行操做仍是串行操做,兩類操做分別對應着須要重寫的evaluateParallel方法與evaluateSequentia方法(evaluateParallel有默認實現),兩個方法均由操做類來實現,而且都須要傳遞一個Spliterator參數。
幕後核心 Spliterator接口
無論並行流仍是串行流,函數調用的過程當中,都無一例外的須要傳遞一個Spliterator參數。第一次看到這裏的時候我以爲很玄,不少時候系統給你提供個類,你不去用,到頭來看源碼卻發現發現原來它纔是萬物起源。這後面的事情,百川歸海,我就不一一細說了,有興趣的讀者能夠自行探索。