「創建線程」是 Java 併發編程的起點。JDK 提供了 繼承 Thread 類 和 實現 Runnable 接口 兩種基礎方式,底層均依賴 Thread 類與操作系統原生線程映射。本文深度解析兩種方式的源碼實現、內存模型差異、資源共享特性、線程生命週期控制,並給出生產環境選型建議。
public class Thread implements Runnable {
// 線程名稱
private char[] name;
// 目標任務(Runnable 實例)
private Runnable target;
// 線程組
private ThreadGroup group;
// 上下文類加載器
private ClassLoader contextClassLoader;
// 線程局部變量映射
ThreadLocal.ThreadLocalMap threadLocals;
// === 構造方法 ===
// 1. 無參:自動生成名稱 "Thread-" + 次數
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
// 2. 指定名稱
public Thread(String name) {
init(null, null, name, 0);
}
// 3. 指定任務
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
// 4. 指定任務 + 名稱
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
// 核心初始化邏輯
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
if (name == null) throw new NullPointerException("name cannot be null");
this.name = name.toCharArray();
this.target = target;
// ... 其他初始化
}
// === 核心 run() 方法 ===
@Override
public void run() {
if (target != null) {
target.run(); // 委託給外部 Runnable 任務
}
// 否則執行子類重寫的 run()(繼承 Thread 方式)
}
// === 啟動線程:native 方法 ===
public synchronized void start() {
if (threadStatus != 0) throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0(); // JNI 調用操作系統創建線程
started = true;
} finally {
if (!started) group.threadStartFailed(this);
}
}
private native void start0();
}
class MyThread extends Thread {
private int counter = 5;
@Override
public void run() {
while (counter-- > 0) {
System.out.println(Thread.currentThread().getName() + ": " + counter);
try { Thread.sleep(100); } catch (InterruptedException ignored) {}
}
}
}
public class ThreadExtendDemo {
public static void main(String[] args) {
Thread t1 = new MyThread();
Thread t2 = new MyThread();
t1.setName("Thread-A");
t2.setName("Thread-B");
t1.start(); // 啟動線程,調用 run()
t2.start();
}
}
| 維度 | 說明 |
|---|---|
| 代碼結構 | 線程對象 = 任務對象,職責合一 |
| 繼承限制 | 無法繼承其他類(Java 單繼承) |
| 資源共享 | 每個 MyThread 實例擁有獨立字段(如 counter),默認不共享 |
| 訪問當前線程 | 直接用 this 或 getName(),無需 Thread.currentThread() |
| 適用場景 | 簡單任務、教學演示、無需共享狀態的獨立線程 |
class MyTask implements Runnable {
private int counter = 5; // 共享狀態
@Override
public void run() {
while (counter-- > 0) {
System.out.println(Thread.currentThread().getName() + ": " + counter);
try { Thread.sleep(100); } catch (InterruptedException ignored) {}
}
}
}
public class RunnableImplDemo {
public static void main(String[] args) {
MyTask task = new MyTask(); // 單一任務實例
Thread t1 = new Thread(task, "Worker-1");
Thread t2 = new Thread(task, "Worker-2");
t1.start();
t2.start();
// 輸出:counter 從 5 遞減到 0,兩線程共享同一變量
}
}
Runnable 實例可被多個 Thread 共享,實現賣票、生產者消費者等經典模型ExecutorService.execute(Runnable)、FutureTask、CompletableFuture 均接受 Runnable() -> { ... } 直接作為 Runnable
MyTask task = new MyTask();
Thread t = new Thread(task);
// 三個關鍵對象區別:
// 1. task:任務邏輯載體,run() 內部 this = task
// 2. t:線程控制對象,start()/join()/interrupt() 針對 t
// 3. Thread.currentThread():正在運行代碼的線程
// - main 調用 t.start() 時:currentThread() = main
// - t 運行 task.run() 時:currentThread() = t
// 每個線程獨立計數器
class IndependentThread extends Thread {
private int count = 5;
public void run() { while (count-- > 0) print(); }
}
new IndependentThread().start(); // count=5
new IndependentThread().start(); // count=5 (獨立副本)
class SharedTask implements Runnable {
private int count = 5; // 共享變量
public void run() {
while (true) {
synchronized(this) { // 必須同步
if (count <= 0) break;
System.out.println(Thread.currentThread().getName() + " sold: " + count--);
}
}
}
}
SharedTask task = new SharedTask();
new Thread(task, "Window-1").start();
new Thread(task, "Window-2").start();
// 輸出:5,4,3,2,1 無重複無遺漏(需 synchronized 保證原子性)
共享模式下,count-- 非原子(讀-改-寫),必須加鎖:
synchronized(this) 鎖任務實例AtomicInteger 無鎖 CASReentrantLock 顯式鎖
Thread t = new Thread(() -> { /* task */ });
t.start(); // 【關鍵】啟動新線程,異步執行 run()
// t.run(); // 【錯誤】直接調用,同步在主線程執行,不創建新線程!
// 生命週期狀態
Thread.State state = t.getState();
// NEW → RUNNABLE → BLOCKED/WAITING/TIMED_WAITING → TERMINATED
| 場景 | 推薦方式 | 理由 |
|---|---|---|
| 業務邏輯任務 | Runnable / Callable + 線程池 | 任務復用、隊列緩衝、拒絕策略、監控指標 |
| 簡單定時/周期任務 | ScheduledExecutorService / Spring @Scheduled | 內置調度、彈性配置 |
| 需要返回值/異常 | Callable + Future / CompletableFuture | 獲取結果、異常傳播、組合編排 |
| 虛擬線程 (JDK 21+) | Thread.ofVirtual().start(Runnable) | 輕量級、百萬併發、無需池化 |
| 教學/原型驗證 | Thread 匿名內部類 / Lambda | 代碼極簡 |
Thread 實例,調用 start0() 請求 OS 創建線程biz-thread-pool-%dThread.startVirtualThread(() -> {}) 成主流理解「任務」與「線程」的分離,是寫出可維護、可擴展、高性能併發代碼的基石。