前言
在日常的工作中,為了提高程式的處理速度,充分利用多核處理器的效能,我們需要手動編寫多執行緒程式碼。但是多執行緒程式設計非常複雜,容易出現死鎖、競態條件等問題,給我們帶來了很大的困擾。而 Java 並行流則提供了一種更加簡單、易用、安全的併發程式設計方式,可以讓我們更加輕鬆地編寫高效的併發程式。
使用多執行緒下載檔案
public class MultiThreadExample {
public static void main(String[] args) throws InterruptedException {
List<String> urls = Arrays.asList(
"https://example.com/file1.txt",
"https://example.com/file2.txt",
"https://example.com/file3.txt",
"https://example.com/file4.txt",
"https://example.com/file5.txt"
);
int threads = 5;
int chunkSize = urls.size() / threads;
int startIndex = 0;
int endIndex = chunkSize;
// 建立執行緒列表
List<DownloadThread> downloadThreads = new ArrayList<>();
// 啟動多個執行緒進行檔案下載
for (int i = 0; i < threads; i++) {
downloadThreads.add(new DownloadThread(urls, startIndex, endIndex));
downloadThreads.get(i).start();
startIndex += chunkSize;
endIndex += chunkSize;
}
// 等待所有執行緒結束並彙總結果
for (DownloadThread downloadThread : downloadThreads) {
downloadThread.join();
}
System.out.println("檔案下載完成");
}
}
class DownloadThread extends Thread {
private List<String> urls;
private int start;
private int end;
public DownloadThread(List<String> urls, int start, int end) {
this.urls = urls;
this.start = start;
this.end = end;
}
@Override
public void run() {
for (int i = start; i < end; i++) {
HttpUtil.download(urls.get(i));
}
}
}
我們首先將要下載的檔案 URL 儲存在一個 List 中,然後為每個塊建立了一個 DownloadThread 物件,並啟動了多個執行緒進行下載操作。每個執行緒只負責處理 URL 的一個塊,呼叫 HttpUtil.download 方法進行檔案下載操作。最後,我們等待所有執行緒結束即可。
使用Fork/Join進行下載
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
public class ForkJoinExample {
public static void main(String[] args) {
List<String> urls = Arrays.asList(
"https://example.com/file1.txt",
"https://example.com/file2.txt",
"https://example.com/file3.txt",
"https://example.com/file4.txt",
"https://example.com/file5.txt"
);
ForkJoinPool pool = new ForkJoinPool();
pool.invoke(new DownloadAction(urls, 0, urls.size()));
System.out.println("檔案下載完成");
}
static class DownloadAction extends RecursiveAction {
private List<String> urls;
private int start;
private int end;
public DownloadAction(List<String> urls, int start, int end) {
this.urls = urls;
this.start = start;
this.end = end;
}
@Override
protected void compute() {
if (end - start <= 1) {
HttpUtil.download(urls.get(start));
return;
}
int mid = (start + end) / 2;
DownloadAction leftAction = new DownloadAction(urls, start, mid);
DownloadAction rightAction = new DownloadAction(urls, mid, end);
invokeAll(leftAction, rightAction);
}
}
}
在這個示例中,我們使用了 ForkJoin 框架來實現檔案下載。首先,我們建立了一個 DownloadAction 類,繼承自 RecursiveAction 類,表示一個遞迴操作。在 compute 方法中,我們首先判斷當前操作的 URL 是否為一個,如果是,則直接呼叫 HttpUtil.download 方法進行檔案下載。如果不是,則將 URL 列表分為兩半,分別建立兩個子任務進行處理,然後使用 invokeAll 方法將這兩個子任務提交到執行緒池中並等待它們完成。
在 main 方法中,我們首先建立了一個 ForkJoinPool 物件,然後呼叫 invoke 方法來執行 DownloadAction 操作。在這裡,我們使用了預設的執行緒池,也可以根據需要建立自定義的執行緒池。
使用Java並行流
import java.util.Arrays;
import java.util.List;
public class ParallelStreamExample {
public static void main(String[] args) {
List<String> urls = Arrays.asList(
"https://example.com/file1.txt",
"https://example.com/file2.txt",
"https://example.com/file3.txt",
"https://example.com/file4.txt",
"https://example.com/file5.txt"
);
urls.parallelStream().forEach(url -> HttpUtil.download(url));
System.out.println("檔案下載完成");
}
}
在這個示例中,我們使用了 Java 並行流來實現檔案下載。首先,我們建立了一個 URL 列表,然後使用 parallelStream 方法將其轉換為並行流。接著,我們使用 forEach 方法遍歷並行流中的每個 URL,並使用 HttpUtil.download 方法進行檔案下載。在這個過程中,Java 會自動將並行流中的元素分配給多個執行緒並行執行,以提高程式的效能。
Java 並行流是什麼?
好了,相信看了上面的案例,應該對並行流有了一個簡單的認識了吧。讓原本又醜又長的程式碼,一下就變得眉清目秀了。所以那讓我們進一步的來了解它吧。
Java 並行流是 Java 8 中新增的一個特性,它提供了一種便捷的方式來進行併發計算。在傳統的 Java 程式設計中,為了利用多核處理器的效能,我們需要手動編寫多執行緒程式碼。但是多執行緒程式設計非常複雜,容易出現死鎖、競態條件等問題,給我們帶來了很大的困擾。而 Java 並行流則提供了一種更加簡單、易用、安全的併發程式設計方式,可以讓我們更加輕鬆地編寫高效的併發程式。
Java 並行流的核心是將資料集合分成多個小塊,然後在多個處理器上並行處理,最後將結果合併成一個結果集。使用 Java 並行流可以有效地利用多核處理器的效能,提升程式執行效率。此外,Java 並行流還提供了一系列的中間操作和終止操作,可以方便地進行資料篩選、對映、過濾等操作。
Java並行流的實現原理?
Java 並行流是基於 Fork/Join 框架實現的,它使用了多執行緒來處理流操作。具體來說,Java 並行流的實現原理如下:
- 拆分資料
當並行流操作開始時,資料會被拆分成多個小塊。每個小塊都會被分配給不同的執行緒去處理。
- 執行任務
每個執行緒會獨立地執行任務。執行緒會使用 fork/join 框架將自己的任務拆分成更小的子任務,並將這些子任務分配給其他執行緒。
- 合併結果
當所有執行緒完成任務後,它們會將自己的結果合併到一起。這個過程類似於 reduce 操作,不同之處在於它是並行的。
Java 並行流的是基於 Fork/Join 框架實現的,而Fork/Join 框架是 Java 7 引入的一個用於平行計算的框架,它基於工作竊取演算法,可以將一個大任務拆分成多個小任務,每個執行緒獨立地處理一個小任務。在 Java 8 中,透過對 Stream 介面的擴充套件,使得平行計算更加容易實現。
需要注意的是,Java 並行流在執行操作時,會根據當前計算機的 CPU 核心數來確定並行執行緒的數量,如果並行執行緒數量過多,會造成過多的上下文切換,反而會降低程式的效能。因此,在使用並行流時需要注意控制並行執行緒的數量。
三種方式對比
在檔案下載這個例子中,我們使用了多執行緒、ForkJoin 框架和 Java 並行流三種方式來實現。我們來對比一下這三種方式的優缺點。
1. 多執行緒方式
優點:
- 可以手動控制執行緒的數量,適用於對執行緒數量有特殊要求的場景。
- 可以使用執行緒池來重用執行緒,減少執行緒建立和銷燬的開銷。
- 可以使用
wait
和notify
等機制來實現執行緒間的通訊和協作。
缺點:
- 需要手動編寫執行緒的建立和銷燬程式碼,程式碼複雜度較高。
- 執行緒之間的協作和通訊需要手動實現,容易出現死鎖等問題。
- 程式碼的可讀性和可維護性較差。
2. ForkJoin 框架方式
優點:
- 可以自動地將任務拆分成更小的子任務,並將子任務分配給多個執行緒並行執行,簡化了程式碼實現。
- 可以透過調整並行度來最佳化效能,提高程式碼的靈活性。
- 可以使用預設的執行緒池或自定義的執行緒池來管理執行緒。
缺點:
- 不適用於 IO 密集型操作,僅適用於 CPU 密集型操作。
- 執行緒之間的協作和通訊需要手動實現,容易出現死鎖等問題。
3. Java 並行流方式
優點:
- 可以使用函數語言程式設計的方式簡化程式碼實現,程式碼可讀性較高。
- 可以自動地將資料分配給多個執行緒並行處理,簡化了程式碼實現。
- 可以根據需要選擇並行度來最佳化效能。
- 可以透過流水線方式最佳化程式碼效能,提高程式碼的靈活性。
缺點:
- 不適用於 IO 密集型操作,僅適用於 CPU 密集型操作。
- 對於一些特殊的操作,例如排序和去重,可能需要手動調整程式碼才能使用並行流。
總結
Java並行流可以讓多執行緒程式設計變得更加簡單易懂,減少程式設計中的併發問題,提高程式碼質量和可維護性。幫助開發人員更加輕鬆地實現任務並行,充分利用多核處理器的效能,加快程式的執行速度。但是雖然並行流有諸多優點,但是還需要根據具體場景來選擇合適的方式。如果是 IO 密集型操作,我們應該使用多執行緒或者 Java NIO 等技術來實現;如果是 CPU 密集型操作,我們可以使用 ForkJoin 框架或者 Java 並行流來實現。
結尾
如果覺得對你有幫助,可以多多評論,多多點贊哦,也可以到我的主頁看看,說不定有你喜歡的文章,也可以隨手點個關注哦,謝謝。
我是不一樣的科技宅,每天進步一點點,體驗不一樣的生活。我們下期見!