多線程是 Java 並發編程基石。Java 提供兩種創建線程的標準途徑:繼承 Thread 類與實現 Runnable 接口。本文從原理、代碼結構、資源共享、擴展性四個維度深度對比,給出明確選型建議。
NEW → RUNNABLE → BLOCKED/WAITING/TIMED_WAITING → TERMINATED
run()start() 一次,重複調用拋 IllegalThreadStateExceptionclass MyThread extends Thread {
private String name;
public MyThread(String name) { this.name = name; }
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + " → 第" + i + "次執行");
}
}
}
public class ExtendThreadDemo {
public static void main(String[] args) {
new MyThread("線程A").start();
new MyThread("線程B").start();
new MyThread("線程C").start();
}
}
特點:
MyThread 實例class Task implements Runnable {
private int ticket = 100; // 共享資源
@Override
public void run() {
while (ticket > 0) {
System.out.println(Thread.currentThread().getName() + " 賣出第 " + ticket-- + " 張票");
}
}
}
public class RunnableDemo {
public static void main(String[] args) {
Task task = new Task(); // 單一任務實例
new Thread(task, "窗口A").start();
new Thread(task, "窗口B").start();
new Thread(task, "窗口C").start();
}
}
優勢:
Task 實例被多線程引用,ticket 自動共享new Thread(() -> System.out.println("Hi")).start()| 維度 | 繼承 Thread | 實現 Runnable |
|---|---|---|
| 代碼復用 | 差(邏輯綁定線程) | 優(任務獨立) |
| 資源共享 | 需額外傳遞共享對象 | 天然共享同一實例 |
| 繼承限制 | 佔用單繼承名額 | 無影響 |
| 線程池適配 | 不支援直接提交 | 完美支援 execute(runnable) |
| Lambda 表達式 | 不支援 | 完美支援 |
| 適用場景 | 簡單獨立線程、教學演示 | 所有生產級並發場景 |
Runnable 無返回值,需結果時用 Callable:
public class CallableDemo {
public static void main(String[] args) throws Exception {
Callable task = () -> {
Thread.sleep(1000);
return 42;
};
FutureTask future = new FutureTask<>(task);
new Thread(future).start();
System.out.println("主線程繼續工作...");
System.out.println("任務結果: " + future.get()); // 阻塞獲取結果
}
}
// 避免直接 new Thread,使用線程池
ExecutorService pool = Executors.newFixedThreadPool(4); // 或自定義 ThreadPoolExecutor
// 提交 Runnable
pool.execute(() -> System.out.println("無返回值任務"));
// 提交 Callable 獲取 Future
Future future = pool.submit(() -> {
Thread.sleep(500);
return "有返回值";
});
try {
System.out.println(future.get(2, TimeUnit.SECONDS));
} catch (TimeoutException e) {
future.cancel(true);
} finally {
pool.shutdown(); // 優雅關閉
}
ticket-- 非原子,需 AtomicInteger 或 synchronizedshutdown()Thread.setDefaultUncaughtExceptionHandler一律使用 Runnable/Callable + 線程池。繼承 Thread 僅適合教學驗證。現代 Java 並發編程核心是「任務提交給執行器」,而非「手動管理線程」。掌握 ExecutorService、CompletableFuture、parallelStream 才是生產級技能。