【Java8新特性】關於並行流與串列流,你必須掌握這些!!

寫在前面

提到Java8,我們不得不說的就是Lambda表達式和Stream API。而在Java8中,對於並行流和串列流同樣做了大量的優化。對於並行流和串列流的知識,也是在面試過程中,經常被問到的知識點。當然,我們不能只是為了應付面試來學習這些知識,更重要的是將這些知識運用到實際的工作中,更好的提高我們的工作效率和工作品質。

什麼是並行流?

簡單來說,並行流就是把一個內容分成多個數據塊,並用不同的執行緒分別處理每個數據塊的流。

Java 8 中將並行進行了優化,我們可以很容易的對數據進行並行操作。 Stream API 可以聲明性地通過 parallel() 與sequential() 在並行流與順序流之間進行切換 。

Fork/Join 框架

Fork/Join 框架: 就是在必要的情況下,將一個大任務,進行拆分(fork)成若干個小任務(拆到不可再拆時),再將一個個的小任務運算的結果進行 join 匯總 。

在這裡插入圖片描述

Fork/Join 框架與傳統執行緒池有啥區別?

採用 「工作竊取」模式(work-stealing):

當執行新的任務時它可以將其拆分成更小的任務執行,並將小任務加到執行緒隊列中,然後再從一個隨機執行緒的隊列中偷一個並把它放在自己的隊列中。

相對於一般的執行緒池實現,fork/join框架的優勢體現在對其中包含的任務的處理方式上。在一般的執行緒池中,如果一個執行緒正在執行的任務由於某些原因無法繼續運行,那麼該執行緒會處於等待狀態。而在fork/join框架的實現中,如果某個子任務由於等待另外一個子任務的完成而無法繼續運行。那麼處理該子問題的執行緒會主動尋找其他尚未運行的子任務來執行。這種方式減少了執行緒的等待時間,提高了程式的性能。

Fork/Join框架實例

了解了ForJoin框架的原理之後,我們就來手動寫一個使用Fork/Join框架實現累加和的示常式序,以幫助讀者更好的理解Fork/Join框架。好了,不廢話了,上程式碼,大家通過下面的程式碼好好體會下Fork/Join框架的強大。

package io.binghe.concurrency.example.aqs;
 
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;
@Slf4j
public class ForkJoinTaskExample extends RecursiveTask<Integer> {
    public static final int threshold = 2;
    private int start;
    private int end;
    public ForkJoinTaskExample(int start, int end) {
        this.start = start;
        this.end = end;
    }
    @Override
    protected Integer compute() {
        int sum = 0;
        //如果任務足夠小就計算任務
        boolean canCompute = (end - start) <= threshold;
        if (canCompute) {
            for (int i = start; i <= end; i++) {
                sum += i;
            }
        } else {
            // 如果任務大於閾值,就分裂成兩個子任務計算
            int middle = (start + end) / 2;
            ForkJoinTaskExample leftTask = new ForkJoinTaskExample(start, middle);
            ForkJoinTaskExample rightTask = new ForkJoinTaskExample(middle + 1, end);
 
            // 執行子任務
            leftTask.fork();
            rightTask.fork();
 
            // 等待任務執行結束合併其結果
            int leftResult = leftTask.join();
            int rightResult = rightTask.join();
 
            // 合併子任務
            sum = leftResult + rightResult;
        }
        return sum;
    }
    public static void main(String[] args) {
        ForkJoinPool forkjoinPool = new ForkJoinPool();
 
        //生成一個計算任務,計算1+2+3+4
        ForkJoinTaskExample task = new ForkJoinTaskExample(1, 100);
 
        //執行一個任務
        Future<Integer> result = forkjoinPool.submit(task);
 
        try {
            log.info("result:{}", result.get());
        } catch (Exception e) {
            log.error("exception", e);
        }
    }
}

Java8中的並行流實例

Java8對並行流進行了大量的優化,並且在開發上也極大的簡化了程式設計師的工作量,我們只需要使用類似如下的程式碼就可以使用Java8中的並行流來處理我們的數據。

LongStream.rangeClosed(0, 10000000L).parallel().reduce(0, Long::sum);

在Java8中如何優雅的切換並行流和串列流呢?

Stream API 可以聲明性地通過 parallel() 與sequential() 在並行流與串列流之間進行切換 。

寫在最後

如果覺得文章對你有點幫助,請微信搜索並關注「 冰河技術 」微信公眾號,跟冰河學習Java8新特性。

最後,附上Java8新特性核心知識圖,祝大家在學習Java8新特性時少走彎路。

在這裡插入圖片描述

Tags: