Java 線程基本概念、使用方法最全詳解
一、什么是線程
線程是程序執(zhí)行的最小單位,是進(jìn)程中的一個執(zhí)行流。一個進(jìn)程可以包含多個線程,這些線程共享進(jìn)程的資源,但有各自的執(zhí)行路徑。
二、Java線程的基本使用
1. 創(chuàng)建線程的方式
(1)繼承 Thread 類
class MyThread extends Thread {
@Override
public void run() {
System.out.println("線程運(yùn)行:" + Thread.currentThread().getName());
}
}
MyThread t1 = new MyThread();
t1.start(); // 啟動線程(2)實(shí)現(xiàn) Runnable 接口
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("線程運(yùn)行:" + Thread.currentThread().getName());
}
}
Thread t2 = new Thread(new MyRunnable());
t2.start();(3)實(shí)現(xiàn) Callable 接口(帶返回值)
import java.util.concurrent.*;
class MyCallable implements Callable<Integer> {
@Override
public Integer call() {
System.out.println("線程運(yùn)行:" + Thread.currentThread().getName());
return 123;
}
}
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(new MyCallable());
Integer result = future.get(); // 阻塞獲取返回值
executor.shutdown();2. 線程的生命周期狀態(tài)
- NEW:新建
- RUNNABLE:可運(yùn)行(正在運(yùn)行或等待CPU)
- BLOCKED:阻塞,等待鎖
- WAITING:等待(無超時,等待其他線程喚醒)
- TIMED_WAITING:超時等待(如 sleep、wait(long))
- TERMINATED:終止
3. 線程常用方法
start():啟動線程run():線程執(zhí)行體(不要直接調(diào)用)sleep(ms):讓當(dāng)前線程休眠join():等待指定線程執(zhí)行完畢interrupt():中斷線程isAlive():判斷線程是否還在運(yùn)行yield():讓出CPU執(zhí)行權(quán)
三、線程的同步和安全
由于多個線程共享內(nèi)存,容易出現(xiàn)線程安全問題,常用的同步方式有:
synchronized關(guān)鍵字(方法或代碼塊)Lock接口(如ReentrantLock)- 原子類(如
AtomicInteger) - 并發(fā)集合(如
ConcurrentHashMap)
四、線程間通信
常用方式:
wait()、notify()、notifyAll()(Object類方法,配合synchronized使用)Condition(配合Lock使用)
五、線程狀態(tài)
Java線程的六種狀態(tài)(Thread.State 枚舉)
- NEW(新建)
- 線程對象剛被創(chuàng)建,還沒有調(diào)用
start()方法。
- 線程對象剛被創(chuàng)建,還沒有調(diào)用
- RUNNABLE(可運(yùn)行)
- 調(diào)用
start()方法后,線程處于可運(yùn)行狀態(tài),可能正在運(yùn)行,也可能正在等待操作系統(tǒng)分配CPU資源。
- 調(diào)用
- BLOCKED(阻塞)
- 線程在等待獲取一個監(jiān)視器鎖(即 synchronized),無法繼續(xù)執(zhí)行。
- WAITING(等待)
- 線程在等待其他線程執(zhí)行特定動作(如
Object.wait()、Thread.join()、LockSupport.park()),不會自動喚醒,需其他線程喚醒。
- 線程在等待其他線程執(zhí)行特定動作(如
- TIMED_WAITING(計時等待)
- 線程在指定時間內(nèi)等待(如
Thread.sleep(long)、Object.wait(long)、Thread.join(long)、LockSupport.parkNanos()),時間到后自動喚醒。
- 線程在指定時間內(nèi)等待(如
- TERMINATED(終止)
- 線程執(zhí)行完畢或出現(xiàn)異常,生命周期結(jié)束。
狀態(tài)轉(zhuǎn)換示意圖
NEW ↓ RUNNABLE ←→ BLOCKED ↓ ? WAITING ←→ TIMED_WAITING ↓ TERMINATED
示例代碼
Thread t = new Thread(() -> {
System.out.println("線程運(yùn)行中");
});
System.out.println(t.getState()); // NEW
t.start();
System.out.println(t.getState()); // RUNNABLE線程狀態(tài)查詢
通過 Thread.getState() 方法可以獲得當(dāng)前線程的狀態(tài),返回值是上面這些枚舉類型。
使用 jstack 工具
jstack 是 JDK 自帶的線程堆棧分析工具。它可以輸出所有線程的堆棧信息,包括線程狀態(tài)和鎖的詳細(xì)信息。
步驟:
找到你的 Java 進(jìn)程 PID(比如用 jps 命令)。
執(zhí)行:
jstack <PID>
在輸出中查找 BLOCKED 狀態(tài)的線程,會顯示類似:
"Thread-1" #12 prio=5 os_prio=0 tid=0x000000001a2b3800 nid=0x1c34 BLOCKED on object monitor owned by "Thread-2" tid=0x000000001a2b4000 at com.example.MyClass.myMethod(MyClass.java:23) - blocked on <0x000000076b1c1b20> (a java.lang.Object) - locked <0x000000076b1c1b20> (a java.lang.Object)
blocked on <0x000000076b1c1b20> 表示線程正在等待這個對象的鎖。
owned by "Thread-2" 表示這個鎖當(dāng)前被哪個線程持有。
2. 使用可視化工具
- VisualVM、JConsole 等工具可以圖形化地查看線程狀態(tài)和鎖的持有情況。
- 在“線程”面板里,可以看到線程狀態(tài),點(diǎn)擊具體線程可以查看堆棧和鎖情況。
3. 代碼層面輔助排查
雖然不能直接在 Java 代碼中獲取“當(dāng)前 BLOCKED 等待哪個鎖”,但可以通過合理設(shè)計日志、監(jiān)控代碼,輔助定位。例如:
- 在 synchronized 代碼塊前后打印日志,標(biāo)記線程進(jìn)入和退出鎖的時間。
- 利用
ThreadMXBean獲取線程堆棧信息(但鎖對象地址需結(jié)合堆棧分析)。
4. 結(jié)合堆棧分析
通過堆??梢钥吹骄€程卡在哪一行代碼,通常是 synchronized 相關(guān)的代碼塊或方法。
六、什么是線程池
線程池(Thread Pool)是一種線程管理方式。它預(yù)先創(chuàng)建一批線程,任務(wù)來了就復(fù)用這些線程,而不是每次都新建/銷毀線程,從而提高性能,減少系統(tǒng)開銷。
七、線程池的核心優(yōu)勢
- 降低資源消耗:避免頻繁創(chuàng)建/銷毀線程。
- 提高響應(yīng)速度:任務(wù)到來時可直接復(fù)用現(xiàn)有線程。
- 便于管理:可統(tǒng)一分配、調(diào)度線程,提高系統(tǒng)穩(wěn)定性。
八、Java中的線程池實(shí)現(xiàn)
Java 提供了線程池相關(guān)的 API,主要在 java.util.concurrent 包下。
1. 主要類和接口
- Executor:線程池的頂層接口,定義了任務(wù)提交的方法。
- ExecutorService:繼承 Executor,增加了生命周期管理等功能。
- ThreadPoolExecutor:線程池的核心實(shí)現(xiàn)類,可高度定制。
- Executors:工具類,提供常用線程池的工廠方法。
2. 常用線程池創(chuàng)建方式
ExecutorService pool = Executors.newFixedThreadPool(5); // 固定大小線程池 ExecutorService pool = Executors.newCachedThreadPool(); // 可變大小線程池 ExecutorService pool = Executors.newSingleThreadExecutor(); // 單線程池 ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2); // 定時任務(wù)線程池
建議: 實(shí)際項(xiàng)目中推薦直接使用
ThreadPoolExecutor,而不是Executors工廠方法,因?yàn)榭梢愿?xì)致地配置參數(shù),避免資源耗盡等風(fēng)險。
九、ThreadPoolExecutor核心參數(shù)
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, // 核心線程數(shù)
maximumPoolSize, // 最大線程數(shù)
keepAliveTime, // 非核心線程存活時間
unit, // 時間單位
workQueue, // 任務(wù)隊(duì)列
threadFactory, // 線程工廠
handler // 拒絕策略
);- corePoolSize:常駐線程數(shù)。
- maximumPoolSize:最大線程數(shù)。
- keepAliveTime:非核心線程閑置多久后回收。
- workQueue:任務(wù)隊(duì)列,如
LinkedBlockingQueue。 - threadFactory:自定義線程創(chuàng)建方式。
- handler:拒絕策略(任務(wù)太多無法處理時的應(yīng)對措施)。
十、線程池工作流程圖
提交任務(wù) ↓ 線程池檢查是否有空閑線程 ↓ 有 → 直接執(zhí)行 無 → 進(jìn)入任務(wù)隊(duì)列 ↓ 隊(duì)列滿 → 創(chuàng)建新線程(不超過最大線程數(shù)) ↓ 線程池滿/隊(duì)列滿 → 拒絕策略
十一、線程池常用方法
- execute(Runnable command):提交任務(wù),無返回值。
- submit(Callable task):提交任務(wù),有返回值(Future)。
- shutdown():平滑關(guān)閉線程池,等待任務(wù)執(zhí)行完畢。
- shutdownNow():立即關(guān)閉,嘗試打斷正在執(zhí)行的任務(wù)。
十二、簡單示例
ExecutorService pool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
pool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 執(zhí)行任務(wù)");
});
}
pool.shutdown();十二、線程池使用注意事項(xiàng)
- 合理配置參數(shù):結(jié)合實(shí)際業(yè)務(wù)和機(jī)器資源設(shè)置線程池參數(shù)。
- 避免任務(wù)堆積:隊(duì)列過長可能導(dǎo)致內(nèi)存溢出。
- 自定義拒絕策略:根據(jù)業(yè)務(wù)需求處理無法執(zhí)行的任務(wù)。
- 線程安全:任務(wù)本身要保證線程安全。
十三、總結(jié)
- 線程池是高效管理線程的關(guān)鍵工具。
- 推薦使用
ThreadPoolExecutor并合理配置參數(shù)。 - 線程池的合理使用能顯著提升系統(tǒng)性能和穩(wěn)定性。
十四、什么是拒絕策略?
當(dāng)線程池中的任務(wù)數(shù)量超過了線程池的最大承載能力(即:線程池最大線程數(shù)和任務(wù)隊(duì)列都滿了),此時再提交新任務(wù),線程池就會觸發(fā)拒絕策略,決定如何處理這些無法立即執(zhí)行的任務(wù)。
十五、拒絕策略的觸發(fā)條件
線程池執(zhí)行任務(wù)的流程大致如下:
- 線程數(shù) < corePoolSize:創(chuàng)建新線程執(zhí)行任務(wù)。
- 線程數(shù) ≥ corePoolSize,隊(duì)列未滿:任務(wù)入隊(duì)列等待執(zhí)行。
- 隊(duì)列滿,線程數(shù) < maximumPoolSize:創(chuàng)建新線程執(zhí)行任務(wù)。
- 線程數(shù) = maximumPoolSize,隊(duì)列也滿:觸發(fā)拒絕策略。
十六、Java內(nèi)置的4種拒絕策略
Java通過java.util.concurrent.ThreadPoolExecutor提供了4種常用的拒絕策略,分別實(shí)現(xiàn)了RejectedExecutionHandler接口:
1. AbortPolicy(默認(rèn)策略)
直接拋出異常(
RejectedExecutionException),阻止系統(tǒng)正常運(yùn)行。
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}適用場景:希望程序員能及時發(fā)現(xiàn)問題,適合對任務(wù)丟失不能容忍的場合。
2. CallerRunsPolicy
由調(diào)用線程(提交任務(wù)的線程)自己執(zhí)行該任務(wù),不會拋異常。
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}適用場景:希望不丟任務(wù),但會降低提交任務(wù)線程的速度,間接緩解線程池壓力。
3. DiscardPolicy
直接丟棄任務(wù),不做任何處理,也不拋異常。
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
// 什么都不做,任務(wù)被丟棄
}
}適用場景:對丟失部分任務(wù)可以容忍,不希望拋異常影響主流程。
4. DiscardOldestPolicy
丟棄隊(duì)列中最早的(最舊的)一個任務(wù),然后嘗試再次提交當(dāng)前任務(wù)。
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll(); // 移除隊(duì)列頭部的一個任務(wù)
e.execute(r); // 再嘗試提交當(dāng)前任務(wù)
}
}
}適用場景:優(yōu)先保證新任務(wù)執(zhí)行,適合對舊任務(wù)時效性要求不高的場合。
十七、自定義拒絕策略
你可以實(shí)現(xiàn) RejectedExecutionHandler 接口,自定義處理邏輯,例如:
- 記錄日志報警
- 任務(wù)入庫持久化
- 發(fā)送告警郵件
- 其他業(yè)務(wù)補(bǔ)償措施
示例代碼:
public class MyRejectHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("任務(wù)被拒絕:" + r.toString());
// 這里可以做日志、報警、持久化等操作
}
}使用方法:
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
new MyRejectHandler()
);十八、實(shí)際應(yīng)用建議
- 高并發(fā)場景建議合理設(shè)置線程池參數(shù),避免頻繁觸發(fā)拒絕策略。
- 對于關(guān)鍵任務(wù),可用 CallerRunsPolicy 或自定義策略,避免任務(wù)丟失。
- 對于非關(guān)鍵任務(wù),可用 DiscardPolicy,保證主流程不被影響。
- 監(jiān)控與報警:拒絕策略中建議加日志和報警,便于及時發(fā)現(xiàn)問題。
到此這篇關(guān)于Java 線程最全詳解的文章就介紹到這了,更多相關(guān)Java 線程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 獲取網(wǎng)絡(luò)302重定向URL的方法
在本篇文章里小編給大家整理的是關(guān)于Java 獲取網(wǎng)絡(luò)302重定向URL的方法以及相關(guān)知識點(diǎn),有興趣的朋友們參考下。2019-08-08
java中l(wèi)ombok的@Data引發(fā)問題詳解
這篇文章主要給大家介紹了關(guān)于java中l(wèi)ombok的@Data引發(fā)問題的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
springboot 返回json格式數(shù)據(jù)時間格式配置方式
這篇文章主要介紹了springboot 返回json格式數(shù)據(jù)時間格式配置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
Java語言實(shí)現(xiàn)非遞歸實(shí)現(xiàn)樹的前中后序遍歷總結(jié)
今天小編就為大家分享一篇關(guān)于Java語言實(shí)現(xiàn)非遞歸實(shí)現(xiàn)樹的前中后序遍歷總結(jié),小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-01-01

