為什麼您可能需要 1 個線程的 ExecutorService?
您可以使用Executors.newSingleThreadExecutor方法創建一個ExecutorService,它帶有一個包含單個線程的池。pool的邏輯如下:
- 該服務一次只執行一項任務。
- 如果我們提交N個任務執行,所有N個任務都會被單線程一個接一個地執行。
- 如果線程被中斷,將創建一個新線程來執行任何剩餘的任務。
讓我們想像一下我們的程序需要以下功能的情況:
我們需要在 30 秒內處理用戶請求,但單位時間內不超過一個請求。
我們創建一個任務類來處理用戶請求:
class Task implements Runnable {
private final int taskNumber;
public Task(int taskNumber) {
this.taskNumber = taskNumber;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
System.out.printf("Processed request #%d on thread id=%d\\n", taskNumber, Thread.currentThread().getId());
}
}
該類對處理傳入請求的行為進行建模並顯示其編號。
接下來,在main方法中,我們為 1 個線程創建一個ExecutorService,我們將使用它來順序處理傳入的請求。由於任務條件規定“30秒以內”,我們加上30秒等待,然後強行停止ExecutorService。
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 1_000; i++) {
executorService.execute(new Task(i));
}
executorService.awaitTermination(30, TimeUnit.SECONDS);
executorService.shutdownNow();
}
啟動程序後,控制台顯示有關請求處理的消息:
線程 ID = 16 上的已處理請求 #1 線程
ID = 16 上的已處理請求 #2
...線程 ID =
16 上的已處理請求 #29
處理請求 30 秒後,executorService調用shutdownNow()方法,該方法停止當前任務(正在執行的任務)並取消所有掛起的任務。之後,程序成功結束。
但一切並不總是那麼完美,因為我們的程序很容易出現這樣的情況,即我們池中唯一的線程拾取的任務之一工作不正確,甚至終止我們的線程。我們可以模擬這種情況來弄清楚executorService在這種情況下如何與單個線程一起工作。
為此,在執行其中一項任務時,我們使用不安全且過時的Thread.currentThread().stop()方法終止我們的線程。我們這樣做是為了模擬其中一個任務終止線程的情況。
我們將更改Task類中的運行方法:
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
if (taskNumber == 5) {
Thread.currentThread().stop();
}
System.out.printf("Processed request #%d on thread id=%d\\n", taskNumber, Thread.currentThread().getId());
}
我們將中斷任務#5。
讓我們看看線程在任務 #5 結束時被中斷的輸出是什麼樣子的:
線程 id=16 上的已
處理請求 #1 線程 id=16 上的已
處理請求 #2 線程 id=16 上的已處理請求 #3 線程 id=16 上的
已處理請求 #4 已
處理請求 #6線程 id=17
線程 id=17 上的已處理請求 #7
…
線程 id=17 上已處理的請求 #29
我們看到線程在任務 5 結束時被中斷後,任務開始在標識符為 17 的線程中執行,儘管它們之前是在標識符為 16 的線程上執行的。並且因為我們的池有一個單線程,這只能說明一件事:executorService用一個新的線程替換了停止的線程並繼續執行任務。
因此,當我們想要順序處理任務並且一次只處理一個任務時,我們應該使用帶有單線程池的 newSingleThreadExecutor,並且我們想要繼續處理隊列中的任務而不管前一個任務是否完成(例如,一個我們的任務殺死了線程)。
線程工廠
說到創建和重新創建線程,我們不能不提線程工廠.
A線程工廠是一個按需創建新線程的對象。
我們可以創建自己的線程創建工廠並將其實例傳遞給Executors.newSingleThreadExecutor(ThreadFactory threadFactory)方法。
|
我們覆蓋創建新線程的方法,將線程名稱傳遞給構造函數。 |
|
我們更改了創建線程的名稱和優先級。 |
所以我們看到我們有 2 個重載的Executors.newSingleThreadExecutor方法。一個沒有參數,第二個帶有ThreadFactory參數。
使用ThreadFactory,您可以根據需要配置創建的線程,例如,通過設置優先級、使用線程子類、向線程添加 UncaughtExceptionHandler等。
GO TO FULL VERSION