開發者都會用到Arrays.sort來對原生數組進行排序,這個API會使用歸並排序或者Tim排序來進行排序,源碼如下所示:
public static void sort(Object[] a) { if (LegacyMergeSort.userRequested) legacyMergeSort(a); else ComparableTimSort.sort(a); }
上面的代碼會依次執行,歸並排序使用了分治的技術。
Java8出來之后,有一個新API用來進行排序,這就是Arrays.ParallelSort,這是一種並行排序,讓我們來看看它是怎么實現的。
Arrays.ParallelSort使用了Java7的Fork/Join框架使排序任務可以在現場池中的多個線程中進行,Fork/Join實現了一種任務竊取算法,一個閑置的線程可以竊取其他線程的閑置任務進行處理。
Arrays.parallelSort概述
這個方法使用了一個臨界值,還有一些容量小於這個臨界值的數組,這個臨界值和數組的容量都是計算來用於並行計算的,如下代碼所示:
private static final int getSplitThreshold(int n) { int p = ForkJoinPool.getCommonPoolParallelism(); int t = (p > 1) ? (1 + n / (p << 3)) : n; return t < MIN_ARRAY_SORT_GRAN ? MIN_ARRAY_SORT_GRAN : t; }
一旦數組決定了使用並行排序,那他馬上會被分為幾個部分,然后通知Fork/Join任務對各個部分進行排序,跟着通知另外的Fork/Join任務對排序后的數組進行合並。
Java8使用如下的方法進行實現:
1、將數組分成4個子數組。
2、對前面兩個子數組進行排序然后合並。
3、對后面的兩個進行排序然后合並。
上面着幾個步驟會重復遞歸,每個子數組都要求容量小於上面計算出來的臨界值。
對比結果
我嘗試去對比Arrays.sort和Arrays.parallelSort的排序時間,在一台4CPU的電腦上,使用如下的代碼:
public class ArraysParallelDemo { public static void main(String[] args) throws FileNotFoundException { List<Double> arraySource = new ArrayList<>(); Scanner reader = new Scanner(ClassLoader. getSystemResourceAsStream("java8demo/large_array_input")); while(reader.hasNext()){ String line = reader.nextLine(); String[] strNums = line.split(","); for ( String strN : strNums){ arraySource.add(Double.parseDouble(strN)); } } System.out.println(arraySource.size()); Double [] myArray = new Double[1]; myArray = arraySource.toArray(myArray); long startTime = System.currentTimeMillis(); Arrays.sort(myArray); long endTime = System.currentTimeMillis(); System.out.println("Time take in serial: "+ (endTime-startTime)/1000.0); Double [] myArray2 = new Double[1]; myArray2 = arraySource.toArray(myArray); startTime = System.currentTimeMillis(); Arrays.parallelSort(myArray2); endTime = System.currentTimeMillis(); System.out.println("Time take in parallel: "+ (endTime-startTime)/1000.0); } }
數組容量和時間消耗的一個基准測試如下所示:

在數組容量小於10000的情況下,並行排序和串行排序消耗時間是差不多的,但是當數組容量在10000以上的時候,並行排序就體現出了它的優勢。
