Java 併發:執行緒、執行緒池和執行器全面教程

banq發表於2024-03-18

本指南深入研究了Executor介面的內部工作原理及其各種實現。

併發的基礎知識
想象一下餐廳廚房的單一流程。廚房本身就代表了這個過程,準備食物、洗碗和接受訂單等各種任務同時發生。現在,執行緒作為廚房裡的廚師進來了。每個廚師(執行緒)處理特定的任務,但可以訪問相同的食材和裝置(共享記憶體)。這允許在單個程序中高效執行多個任務。

併發 vs. 並行性

  • 併發:考慮同時下載多個檔案。每次下載都是獨立發生的,但它們都爭奪相同的網際網路頻寬(就像廚房裡的廚師共享資源一樣)。這並不一定意味著下載速度更快,但它允許同時處理所有檔案。
  • 並行性:想象一下在具有多個核心的計算機上處​​理一個大型影片檔案。每個核心可以同時處理影片的不同部分,與處理整個任務的單個核心相比,真正實現了更快的處理速度。在這裡,任務真正並行執行,與共享資源的併發下載不同。

雖然多執行緒帶來了好處,但它也帶來了複雜性:

  • 死鎖:想象一下兩個廚師正在等待對方的工具來完成他們的任務。這就造成了僵局,兩個廚師都無法繼續進行,從而導致整個廚房(流程)停止。
  • 競爭條件:想象兩個收銀員試圖同時更新庫存系統。一個收銀員可能會讀取當前庫存,出售商品,然後嘗試更新庫存,而另一位收銀員也會做同樣的事情。這可能會導致資料不一致(例如,在沒有庫存時顯示一件商品有庫存)。
  • 同步:為了避免這些問題,我們需要像主廚一樣管理任務流程進行協調。同步機制確保執行緒以受控方式訪問共享資源,從而防止死鎖和競爭條件。

揭開執行器介面
在多執行緒的世界裡,執行緒的建立、管理和潛在隱患可能會變得一團糟。這時,Executor(執行器)介面優雅地登場了。它就像一個契約,是程式提交任務以供執行的一種定義明確的方式,而不會陷入執行緒建立和排程的複雜細節中。

Executor 介面的核心功能封裝在 execute(Runnable task) 方法中。從本質上講,該方法將一段程式碼封裝在一個 Runnable 物件中,然後將其交給非同步執行。非同步執行簡單地說就是程式在繼續執行之前不等待任務完成。這樣,當提交的任務在後臺併發執行時,程式仍能保持響應。

根據我的經驗,Executor 介面將任務提交與底層執行緒管理分離開來,從而提供了顯著的優勢。這種分離能使程式碼更簡潔,讓開發人員專注於任務的邏輯,而不是執行緒建立和排程的複雜性。

在此基礎上,ExecutorService 介面擴充套件了 Executor 的功能,提供了更多用於管理任務執行生命週期的方法。其中包括關閉執行器、優雅地終止正在執行的任務以及檢查已提交任務狀態的方法。這些功能為管理 Java 應用程式中的併發執行提供了更全面的方法。

Powerhouse:執行器實現
Java 中的類Executors提供了一系列令人愉快的 Executor 實現,每個實現都滿足特定的應用程式需求。讓我們深入研究這些選項,並探索最適合您的多執行緒烹飪創作(應用程式)的選項。

1. FixThreadPool:可靠的主力
想象一下一個熙熙攘攘的餐廳廚房,有一個專門的廚師團隊(執行緒)。這FixedThreadPool類似於這種情況。它維護固定數量的執行緒(核心池大小),努力處理傳入的任務(請求),而不動態建立新執行緒。這種可預測性使其成為具有預定義工作負載的應用程式的理想選擇。

  • 真實示例:  Web 伺服器通常會經歷相對穩定的使用者流量。A FixedThreadPool 可以配置核心池大小,以有效處理此預期負載,確保平穩執行而不會出現資源過載。

2. CachedThreadPool:按需廚師隊伍
現在,想象一下處理大量影像縮圖的照片編輯應用程式。反映CachedThreadPool了這種動態環境。它以執行緒的核心池大小開始,但神奇之處在於它能夠根據需要建立新執行緒。一旦執行緒完成其任務(縮圖處理),它就可以用於新任務,從而無需不斷維護大量空閒執行緒。這種彈性使其適合具有突發工作負載的應用程式,其特點是任務峰值不可預測。

  • 現實示例: 照片編輯軟體通常會處理數量波動的影像操作。A CachedThreadPool 透過動態擴充套件執行緒數量、最佳化資源利用率來有效處理這些突發。

3. ScheduledThreadPoolExecutor:準時的工頭
想象一個每週自動檢查軟體更新的系統。體現ScheduledThreadPoolExecutor了這一理念。它擅長安排任務在特定延遲後或定期(例如每週更新)執行。該執行器提供對任務排程的細粒度控制,確保及時執行,而不會擾亂應用程式的核心功能。

  • 現實示例: 許多應用程式需要後臺任務以特定的時間間隔執行。A ScheduledThreadPoolExecutor 可以配置為無縫處理這些任務,使主程式免於管理時序。

4. SingleThreadExecutor:有序的抄寫員
維護一個日誌檔案,其中的條目需要按照嚴格的時間順序寫入,這是一個很好的例子SingleThreadExecutor。該執行器確保任務按順序執行,一個接一個。這可以防止可能導致日誌條目混亂或不同步的併發問題。

  • 現實示例:日誌 系統通常需要嚴格的條目排序以保持清晰的審計跟蹤。A SingleThreadExecutor 保證此順序,防止資料損壞或混亂。

選擇合適的執行人:關鍵在於合適
根據我的經驗,選擇合適的執行器對於最佳化多執行緒效能至關重要。請考慮您應用程式的工作負載特徵。

  • 如果您的任務數量可預測,那麼固定執行緒池(FixedThreadPool)就是您可靠的夥伴。
  • 對於波動的工作負載,CachedThreadPool 可以無縫適應。
  • 排程任務可以透過 ScheduledThreadPoolExecutor 找到理想的歸宿,
  • 而確保秩序則需要 SingleThreadExecutor。

關鍵配置引數:微調您的執行器
除了執行器型別的選擇之外,微調其行為也至關重要。以下是一些關鍵配置引數:

  • 核心池大小: 這決定了始終執行的最小執行緒數,保證了處理能力的基線水平。
  • 最大池大小: 這設定了池可以建立的執行緒數的上限,以防止資源耗盡。
  • 保持活動時間: 這定義了空閒執行緒在終止之前等待的時間,影響資源使用和響應能力。

透過了解每種 Executor 型別的優勢並熟練地配置其引數,您可以在 Java 應用程式中編排執行良好的多執行緒交響曲。

我們已經探索了 Executors 類所提供的各種執行器。現在,讓我們深入探討一下每種型別在現實世界中的應用場景,並提供程式碼片段來說明它們的有效用法。請記住,這些都是演示核心概念的簡化示例。

1.FixedThreadPool可預測的執行者

場景:網路伺服器不斷收到使用者對網頁和資料的請求。
最佳選擇:固定執行緒池。我們知道工作量是相對穩定的,因此固定數量的執行緒可以有效地處理請求,而不會產生不必要的開銷。


<font>// Import necessary classes<i>
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class WebServer {

    private static final int NUM_THREADS = 5;
// Adjust based on expected traffic<i>

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);

       
// 模擬處理使用者請求(用實際邏輯代替)<i>
        for (int i = 0; i < 10; i++) {
            Runnable task = () -> System.out.println(
"Handling user request " + i);
            executor.execute(task);
        }

       
// 處理請求後優雅關機<i>
        executor.shutdown();
    }
}


2.CachedThreadPool:動態雙核

  • 情景:一款照片編輯應用程式允許使用者同時調整多張圖片的大小。
  • 最合適:快取執行緒池。圖片的數量可能會變化,因此按需建立執行緒可最佳化資源使用。

<font>// Import necessary classes<i>
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class PhotoEditor {

    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();

       
// Simulate resizing images (replace with actual logic)<i>
        for (int i = 0; i < 10; i++) {
            Runnable task = () -> System.out.println(
"Resizing image " + i);
            executor.execute(task);
        }

       
// Graceful shutdown after processing images<i>
        executor.shutdown();
    }
}


3.ScheduledThreadPoolExecutor:計時員

  • 情景:應用程式需要每週檢查軟體更新。
  • 最佳選擇:ScheduledThreadPoolExecutor.安排任務定期執行以進行更新檢查。

<font>// Import necessary classes<i>
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class UpdateChecker {

    public static void main(String[] args) {
        ExecutorService executor = Executors.newScheduledThreadPool(1);

       
// Schedule a task to check for updates every week<i>
        Runnable task = () -> System.out.println(
"Checking for updates...");
        executor.scheduleAtFixedRate(task, 0, 1, TimeUnit.WEEKS);
// Initial delay, period<i>

       
// Simulate application running for some time<i>
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

       
// Graceful shutdown after some time<i>
        executor.shutdown();
    }
}


4.SingleThreadExecutor:有序的守護者

  • 情景:日誌系統需要按照特定順序將條目寫入檔案。
  • 最佳選擇:單執行緒執行器。確保按順序寫入條目,防止資料損壞。

<font>// Import necessary classes<i>
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class LogWriter {

    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();

       
// Simulate writing log entries (replace with actual logic)<i>
        for (int i = 0; i < 10; i++) {
            Runnable task = () -> System.out.println(
"Writing log entry " + i);
            executor.execute(task);
        }

       
// Graceful shutdown after writing logs<i>
        executor.shutdown();
    }
}

注意點:

  • 濫用執行器可能會導致資源利用效率低下、效能問題或死鎖。
  • 不適當的設定可能會導致執行緒匱乏(沒有足夠的執行緒)、資源耗盡(執行緒太多)或過多的任務排隊延遲。
  • 突然終止可能會導致資源掛起和任務未完成。
  • 未處理的異常可能會導致不可預測的行為和潛在的執行緒池崩潰。
  • 長時間執行的任務可能會導致其他等待資源的任務捱餓,從而影響整體效能。
  • 不受監控的執行緒池可能會導致資源耗盡和效能下降。

相關文章