在進到 Coroutine 之前,先看看 Kotlin/JVM 開發者過去是怎麼處理多執行緒。這一節整理三組常用工具:Kotlin 的 thread {}、Java 的 Thread、以及 java.util.concurrent 提供的 Executors。它們都還會在後續比較 Coroutine 時當成對照組。
Kotlin 的 thread {}#
Kotlin 標準函式庫提供了 kotlin.concurrent.thread,是建立執行緒(Thread)的最短寫法。
import kotlin.concurrent.thread
fun runInBackground() {
thread {
// 這段程式會在新的執行緒上執行
println("Hello from ${Thread.currentThread().name}")
}
}幾個要注意的點:
thread {}預設會立刻啟動,相當於 Javanew Thread(...).start()。- 它不允許從 lambda 直接回傳值,因為 lambda 的型別等同
() -> Unit。 - 想等執行緒完成,可以保留
Thread變數呼叫join()。
val worker = thread(start = false) {
// 預先建立但不啟動
}
worker.start()
worker.join() // 等待完成Java 的 Thread#
thread {} 底層其實就是 Java 的 Thread,只是包成 Kotlin 慣用語法。直接用 Java API 也很常見。
public class Worker implements Runnable {
@Override
public void run() {
System.out.println("running on " + Thread.currentThread().getName());
}
}
Thread t = new Thread(new Worker(), "worker-1");
t.start();
t.join();Thread 的能力有限:建立、啟動、等待結束、設定優先權、設定為 daemon。它不會自動把工作排隊,也不會幫忙重用執行緒。
Executors 與執行緒池#
每來一個工作就 new Thread() 並不划算。java.util.concurrent.Executors 提供了幾種常用的執行緒池:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(() -> doWork());
pool.shutdown();常見的工廠方法:
| 工廠方法 | 執行緒配置行為 | 適用場景 |
|---|---|---|
newFixedThreadPool(n) | 固定大小的執行緒池 | CPU 密集任務 |
newCachedThreadPool() | 根據需要建立執行緒,閒置會自動回收 | 短時且大量的任務 |
newSingleThreadExecutor() | 固定一條執行緒,任務會依序執行 | 需要依序執行的任務 |
newScheduledThreadPool(n) | 支援延遲與排程執行 | 延遲 / 定時排程任務 |
回傳的 Future<T> 可以拿到結果或取消任務:
Future<Integer> f = pool.submit(() -> 42);
Integer result = f.get(); // 阻塞直到完成
f.cancel(true); // 嘗試中斷
Executors解決了「執行緒重用」與「任務排隊」的問題,但仍然停留在「以執行緒為單位」的思考方式。要表達「這個任務由很多步驟組成、每一步都不要阻塞」這類需求,還得加上回呼或CompletableFuture。
同步寫法為什麼不夠用#
把上述工具套到「登入、抓資料、畫畫面」這種步驟接續的情境,會遇到兩個老問題:
- 阻塞(Blocking):純同步寫法直接
f.get()會把執行緒卡住,等於把多執行緒的好處抵消掉一部分。 - 控制權外放:用回呼(Callback)串接時,每一步都把後續邏輯交給上一步呼叫,導致回呼地獄(Callback Hell)與控制權轉移的問題。
後續可以把
Executors、CompletableFuture想成 Coroutine 的前身。它們解決了部分問題,但缺少語言層級的「掛起 / 恢復」機制,所以寫起來仍然偏底層。
小結#
- Kotlin 的
thread {}是建立執行緒的便利語法,底層仍是 JavaThread。 Executors提供執行緒池與排程能力,避開重複建立執行緒的成本。- 這些工具讓多執行緒可寫,但無法本質上解決「非同步程式如何寫得直覺」的問題,這正是 Coroutine 想接手的位置。
原文出處#
- 原書/iThome:https://ithelp.ithome.com.tw/articles/10259764
- 書本:Kotlin 小宇宙(iThome 鐵人賽系列書)