Java - 線程池

你好,未來的Java開發者!今天,我們將深入探討令人興奮的線程池世界。如果你是編程新手,也別擔心;我會一步一步引導你了解這個概念,就像我多年教學中為無數學生所做的那樣。所以,泡一杯咖啡(或茶,如果你更喜歡),我們開始吧!

Java - Thread Pools

線程池是什麼?

想象一下你正在經營一家繁忙的餐廳。每次有新顧客進來,你都可以雇用一個新的服務員來服務他們。但那會混亂且昂貴!相反,你有一個固定的服務員人數,他們會服務多個顧客。這基本上就是線程池在Java編程中所做的事情。

線程池是一組預先實例化的、可重用的線程,用於执行任務。我們不是為每個任務創建新的線程,這可能會消耗大量資源,而是使用一組現有的線程來執行這些任務。

為什麼在Java中使用線程池?

你可能在想,“為什麼要麻煩使用線程池?我們不能在我們需要時創建新的線程嗎?”好吧,讓我通過我教學經驗中的一個小故事來解釋。

有一次,我有一個學生試圖在他的應用程序中為每個任務創建一個新的線程。他的程序在處理小數據集時運行良好,但當他試圖處理大量數據時,他的電腦幾乎崩潰了!這時,我向他介紹了線程池。

以下是使用線程池的一些主要原因:

  1. 更好的性能:創建和銷毀線程是昂貴的。線程池重用線程,節省開銷。
  2. 資源管理:你可以控制可以同時運行的最大線程數量。
  3. 提高響應性:任務可以立即由正在運行的線程執行。

在Java中創建線程池

Java通過Executors類提供了多種創建線程池的方法。我們來看看三種常見的方法:

1. 使用newFixedThreadPool()方法創建線程池

此方法創建一個具有固定數量線程的線程池。讓我們看看一個例子:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

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

for (int i = 0; i < 10; i++) {
Runnable worker = new WorkerThread("" + i);
executor.execute(worker);
}

executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println("已結束所有線程");
}
}

class WorkerThread implements Runnable {
private String message;

public WorkerThread(String s) {
this.message = s;
}

public void run() {
System.out.println(Thread.currentThread().getName() + " (開始) 訊息 = " + message);
processMessage();
System.out.println(Thread.currentThread().getName() + " (結束)");
}

private void processMessage() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

在這個例子中,我們創建了一個具有5個線程的固定線程池。然後我們向這個池提交了10個任務。池將使用其5個線程來執行這10個任務,當線程可用時就重用它們。

2. 使用newCachedThreadPool()方法創建線程池

此方法創建一個線程池,根據需要創建新線程,但也會在可能的情况下重用先前構建的線程。讓我們看看它是如何工作的:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

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

for (int i = 0; i < 100; i++) {
executor.execute(new Task());
}

executor.shutdown();
}
}

class Task implements Runnable {
public void run() {
System.out.println("線程名稱: " + Thread.currentThread().getName());
}
}

在這個例子中,我們創建了一個緩存線程池並向其提交了100個任務。池將在需要時創建新線程,並在可能的情况下重用它們。

3. 使用newScheduledThreadPool()方法創建線程池

此方法創建一個可以安排在給定延遲後運行或定期執行命令的線程池。這裡有一個例子:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

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

Runnable task = () -> System.out.println("執行任務於 " + System.nanoTime());

executor.scheduleAtFixedRate(task, 0, 2, TimeUnit.SECONDS);
}
}

在這個例子中,我們創建了一個具有一個線程的計劃線程池。然後我們安排了一個任務每2秒運行一次。

ExecutorService中的方法

以下是ExecutorService接口中的一些重要方法:

方法 描述
execute(Runnable) 將在未來的某個時間點執行給定的命令
submit(Callable) 提交一個返回值的任務以供執行,並返回一個Future
shutdown() 開始有序關閉,在此過程中先前的任務將被執行,但不再接受新任務
shutdownNow() 對正在執行的任務嘗試停止,並中斷等待任務的處理
isShutdown() 如果此執行器已關閉,則返回true
isTerminated() 如果關閉後所有任務都已完成的話,則返回true

結論

就是這樣,各位!我們穿越了Java線程池的土地,從了解它們為什麼有用到以不同的方式創建和使用它們。記住,就像我們的餐廳比喻一樣,線程池幫助我們有效地管理資源。

在我多年的教學中,我見證了學生們從基本的線程概念掙扎到建立複雜、高效的多線程應用程序。通過練習和耐心,你也會達到那個階段!

當你繼續你的Java旅程時,請繼續探索和實驗。線程池只是Java為並發編程提供的廣闊工具箱中的一個工具。愉快的編程,願你的線程始終有效地池化!

Credits: Image by storyset