大佬們好!我是LKJ_Coding,一枚初級馬牛,正在努力在代碼的叢林中找尋自己的方向。如果你也曾在調試中迷失,或是在文檔中翻滾,那我們一定有許多共同話題可以聊!今天,我帶着滿滿的代碼“乾貨”來和大家分享,學不學無所謂,反正我先吐槽了!
前言
説起 Java 多線程,很多人第一反應就是那句經典老梗:
“哥們,我寫了個多線程程序,跑得賊快,但我不敢動它。”
沒錯,多線程這玩意兒,在簡歷上是高大上的標籤,在項目裏卻是“出事找不到根”的潛在爆炸物。你不懂的時候,用起來像開盲盒;你真懂了,才知道:多線程從來不是速度的代名詞,而是複雜性的倍增器。
今天我們不光要“談笑風生”,還要代碼實戰,把 Java 多線程真正掰開揉碎講清楚。你準備好了嗎?
🔥 1. Thread 與 Runnable:你還在用 Thread.start() 嗎?
Java 最原始的多線程實現方式有兩種:
- 繼承
Thread類 - 實現
Runnable接口
這兩者的區別,面試官問爛了,但咱得真講明白:本質上都是給 Thread 提供一個“任務”而已,關鍵在於任務與線程的分離是否清晰。
🧪 案例對比
✅ 繼承 Thread
class MyThread extends Thread {
@Override
public void run() {
System.out.println("我是繼承 Thread 的線程");
}
}
new MyThread().start();
優點:簡單、直接 缺點:Java 只能單繼承,擴展性弱
✅ 實現 Runnable
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我是實現 Runnable 的線程");
}
}
new Thread(new MyRunnable()).start();
優點:任務與線程分離,結構更清晰,可複用性強 缺點:稍微多寫一行代碼(但工程師怕寫代碼你圖什麼)
🧠 建議
- 實際開發中推薦使用 Runnable 或 Callable(支持返回值)
- 避免繼承 Thread 導致的類層次混亂
- 實現 Runnable 更易與線程池集成
⏳ 2. 線程生命週期:你以為 start() 就結束了?天真!
線程的生命週期,像極了人的職場軌跡:新建 → 就緒 → 運行 → 阻塞 → 死亡
🌀 六大狀態詳解
| 狀態 | 説明 |
|---|---|
| New | 新建,剛 new 出來,還沒 start |
| Runnable | 可運行,等 CPU 調度 |
| Running | 正在執行(但實際在 JVM 中是 Runnable) |
| Blocked | 被鎖阻塞(等別人釋放鎖) |
| Waiting | 無限等待(比如 join) |
| Timed Waiting | 有時限地等待 |
| Terminated | 死了,執行完 run 或異常崩了 |
🌰 示例代碼演示狀態流轉
public class ThreadLifecycle {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (Exception ignored) {}
});
System.out.println("1. " + t.getState()); // NEW
t.start();
System.out.println("2. " + t.getState()); // RUNNABLE
Thread.sleep(100);
System.out.println("3. " + t.getState()); // TIMED_WAITING
Thread.sleep(1500);
System.out.println("4. " + t.getState()); // TERMINATED
}
}
🧱 3. 線程池原理與使用:別再用 new Thread() 了,池子才是正解
你每次都 new Thread() 就像你每次泡茶都去山裏挑水——能用,但累。
線程池的本質是複用線程資源、控制併發數量、提升性能、方便管理。
🧠 Executors 快速入門(不推薦的便捷方式)
ExecutorService pool = Executors.newFixedThreadPool(5);
pool.submit(() -> System.out.println("幹活!"));
注意:生產建議別用 Executors 提供的工廠方法!
newFixedThreadPool可能 OOM(無界隊列)newCachedThreadPool會無限開線程(容易撐爆)
✅ 推薦方式:自己定義 ThreadPoolExecutor
ExecutorService pool = new ThreadPoolExecutor(
2, // 核心線程數
5, // 最大線程數
60L, // 空閒線程存活時間
TimeUnit.SECONDS, // 時間單位
new LinkedBlockingQueue<>(10), // 隊列容量
Executors.defaultThreadFactory(), // 線程工廠
new ThreadPoolExecutor.AbortPolicy() // 拒絕策略
);
🧠 拒絕策略有這些(掌握了就是面試利器)
| 策略名 | 作用 |
|---|---|
| AbortPolicy | 拋出異常(默認) |
| CallerRunsPolicy | 誰提交誰執行 |
| DiscardPolicy | 靜默丟棄 |
| DiscardOldestPolicy | 丟棄最老的任務,再執行新任務 |
🧩 4. 併發工具類:不是讓你手寫同步邏輯的
Java 從不希望你自己搞“線程協調邏輯”,所以從 JDK 5 開始,提供了一堆現成的工具類。
今天我們聚焦三位主角:
① CountDownLatch —— 倒計時器,常用於主線程等待多個子線程
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println("任務完成!");
latch.countDown(); // 計數減一
}).start();
}
latch.await(); // 等待計數歸零
System.out.println("全部搞定,收工!");
適合場景:多個任務完成後統一彙總
② CyclicBarrier —— 迴環柵欄,大家一起出發
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("所有人都準備好了,出發!");
});
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 到達集合點");
try {
barrier.await(); // 等待其他線程
} catch (Exception ignored) {}
}).start();
}
適合場景:多人分段協作,階段性同步
③ Semaphore —— 信號量,控制通行人數
Semaphore sem = new Semaphore(2); // 同時允許2個線程
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
sem.acquire(); // 獲取通行證
System.out.println(Thread.currentThread().getName() + " 進入臨界區");
Thread.sleep(1000);
sem.release(); // 釋放通行證
} catch (Exception ignored) {}
}).start();
}
適合場景:限流、限資源訪問(如連接池、打印機等)
🧾 結語:併發不是“快”,是“控”
很多人學多線程,就是為了讓程序“快”。但實際上,多線程真正的難點不在於“怎麼開”,而在於“怎麼關、怎麼管、怎麼同步、怎麼不出事”。
它像一頭猛獸,控制得好,你程序如虎添翼;控制不好,那就是“併發地報錯、併發地翻車”。
所以,寫多線程代碼前,請捫心自問一句:
“你是真的需要多線程,還是隻是覺得酷?”
好啦,廢話不多説,今天的分享就到這裏!如果你覺得我這“初級馬牛”還挺有趣,那就請給我點個贊、留個言、再三連擊三連哦!讓我們一起“成精”吧!下次見,不見不散!