天天看點

幹貨|Java線程池的使用方法及注意事項

Java線程池是Java提供的一種線程池實作,可以有效地控制線程的建立和銷毀,提高程式的性能和穩定性。本文将詳細介紹Java線程池的基本概念、使用方法、實作原理以及注意事項,并提供相關代碼示例供參考。

什麼是線程池

線程池是一種用于管理線程的技術,其基本原理是在程式啟動時建立一定數量的線程,并将這些線程放入一個池子中,當有任務需要執行時,從池子中取出一個線程執行任務,任務執行完畢後将線程放回池子中,等待下一次使用。

為什麼要使用線程池

在傳統的多線程程式設計中,每次需要執行一個任務時,都需要建立一個線程,任務完成後再銷毀線程。這樣做的缺點是,線程的建立和銷毀需要耗費大量的資源,同時也會影響程式的性能和穩定性。

線程池的出現解決了這個問題,它能夠控制線程的建立和銷毀,避免了頻繁的建立和銷毀線程,提高了程式的性能和穩定性。

線程池的使用方法

Java提供了Executor架構來實作線程池,通過Executor架構可以友善地建立和管理線程池

建立線程池

Java提供了以下幾種方式來建立線程池:

  1. newFixedThreadPool:建立一個固定大小的線程池,當線程池中的線程全部被占用時,新的任務會在等待隊列中等待。
  2. newSingleThreadExecutor:建立一個隻有一個線程的線程池,所有任務都在這個線程中依次執行。
  3. newCachedThreadPool:建立一個大小不定的線程池,當線程池中的線程全部被占用時,會建立新的線程來執行任務。
  4. newScheduledThreadPool:建立一個定時執行任務的線程池,可以設定線程池的大小和執行的時間。

例如,使用newFixedThreadPool建立一個固定大小為10的線程池:

ExecutorService executorService = Executors.newFixedThreadPool(10);           

送出任務

建立了線程池後,就可以通過executorService.submit()方法來送出任務。submit()方法接受一個Runnable或Callable類型的參數,表示需要執行的任務。

例如,送出一個簡單的列印任務:

executorService.submit(() -> System.out.println("Hello, World!"));           

關閉線程池

在程式結束時,需要手動關閉線程池,可以使用executorService.shutdown()方法來關閉線程池。

executorService.shutdown();           

當任務被送出到線程池中時,線程池會按照預設的規則來處理這些任務,主要包括以下幾個步驟:

  1. 如果目前線程池中的線程數小于corePoolSize,那麼就建立一個新的線程執行任務。
  2. 如果目前線程池中的線程數等于corePoolSize,那麼就将任務加入任務隊列中,等待執行。
  3. 如果任務隊列已滿,并且目前線程池中的線程數小于maximumPoolSize,那麼就建立一個新的線程執行任務。
  4. 如果目前線程池中的線程數等于maximumPoolSize,并且任務隊列也已滿,那麼就根據預設的飽和政策來處理任務。

在預設情況下,線程池的飽和政策為ThreadPoolExecutor.AbortPolicy,該政策會抛出RejectedExecutionException異常,表示無法處理該任務。其他的飽和政策還包括:

  • ThreadPoolExecutor.DiscardPolicy:直接丢棄該任務,不做任何處理。
  • ThreadPoolExecutor.DiscardOldestPolicy:丢棄最老的任務,将目前任務加入任務隊列中。
  • ThreadPoolExecutor.CallerRunsPolicy:将任務傳回給送出該任務的線程執行。

此外,線程池中的線程數量是可以動态調整的。如果線程池中的線程數超過corePoolSize,并且某個線程在執行任務時處于空閑狀态,那麼該線程将被終止,直到線程池中的線程數不大于corePoolSize為止。

在實際開發中,我們可以通過Java提供的ThreadPoolExecutor類來建立和使用線程池。下面是一個簡單的示例代碼:

javaCopy codeimport java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExample {

    public static void main(String[] args) {
        // 建立一個固定大小為10的線程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        // 送出10個任務
        for (int i = 0; i < 10; i++) {
            final int taskNum = i;
            executorService.execute(() -> {
                System.out.println("線程:" + Thread.currentThread().getName() + " 正在執行任務 " + taskNum);
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        // 關閉線程池
        executorService.shutdown();
    }
}
           

在這個示例中,我們建立了一個固定大小為10的線程池,然後向線程池中送出了10個任務。每個任務的執行時間為1秒,我們通過TimeUnit.SECONDS.sleep(1)來模拟任務的執行時間。在送出完所有任務後,我們關閉了線程池。

繼續閱讀