Java中控制多線程順序執(zhí)行的六種實現(xiàn)方案
一、線程順序執(zhí)行的核心挑戰(zhàn)
在多線程編程中,線程的執(zhí)行順序本質(zhì)上是不確定的,由操作系統(tǒng)調(diào)度器決定。但在某些業(yè)務場景中,我們需要確保線程按照特定順序執(zhí)行,例如:
- 分階段任務處理(先初始化,再加載,最后執(zhí)行)
- 事件的有序處理(保證事件處理的先后順序)
- 資源的有序訪問(避免競爭條件)
下面介紹的6種方法都能解決這個問題,但各有特點和適用場景。
二、6種實現(xiàn)方法詳解
方法1:join() - 簡單直接的阻塞方案
實現(xiàn)原理:
join()方法會使當前線程等待目標線程執(zhí)行完畢。通過在主線程中依次調(diào)用各個線程的join(),可以實現(xiàn)嚴格的順序執(zhí)行。
public class JoinExample {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("第一階段任務執(zhí)行");
// 模擬任務執(zhí)行時間
try { Thread.sleep(500); } catch (InterruptedException e) {}
});
Thread t2 = new Thread(() -> System.out.println("第二階段任務執(zhí)行"));
Thread t3 = new Thread(() -> System.out.println("第三階段任務執(zhí)行"));
try {
System.out.println("啟動第一階段任務");
t1.start();
t1.join(); // 主線程在此等待t1完成
System.out.println("啟動第二階段任務");
t2.start();
t2.join(); // 等待t2完成
System.out.println("啟動第三階段任務");
t3.start();
t3.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("任務執(zhí)行被中斷");
}
}
}優(yōu)點:
- 實現(xiàn)簡單直觀
- 不需要額外同步工具
缺點:
- 會阻塞主線程
- 不夠靈活,難以應對復雜場景
適用場景:簡單的線性任務流,且可以接受阻塞主線程的情況。
方法2:單線程線程池 - 優(yōu)雅的任務隊列方案
實現(xiàn)原理:
通過Executors.newSingleThreadExecutor()創(chuàng)建單線程的線程池,自然保證任務按提交順序執(zhí)行。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SingleThreadExecutorExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
System.out.println("準備數(shù)據(jù)");
// 模擬耗時操作
try { Thread.sleep(1000); } catch (InterruptedException e) {}
});
executor.execute(() -> System.out.println("處理數(shù)據(jù)"));
executor.execute(() -> System.out.println("保存結(jié)果"));
executor.shutdown();
// 等待所有任務完成
while (!executor.isTerminated()) {}
System.out.println("所有任務已完成");
}
}優(yōu)點:
- 自動管理線程生命周期
- 支持任務隊列
- 避免手動創(chuàng)建線程
缺點:
- 無法靈活控制中間狀態(tài)
- 單線程可能成為性能瓶頸
適用場景:需要順序執(zhí)行但可能動態(tài)添加任務的場景。
方法3:CountDownLatch - 靈活的同步屏障方案
實現(xiàn)原理:
CountDownLatch通過計數(shù)器實現(xiàn)線程等待,允許一個或多個線程等待其他線程完成操作。
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) {
// 第一個閘門,初始為1表示t1可以直接執(zhí)行
CountDownLatch latch1 = new CountDownLatch(1);
// 第二個閘門,t2需要等待
CountDownLatch latch2 = new CountDownLatch(1);
Thread t1 = new Thread(() -> {
System.out.println("數(shù)據(jù)庫連接建立");
latch1.countDown(); // t1完成后打開閘門1
});
Thread t2 = new Thread(() -> {
try {
latch1.await(); // 等待閘門1打開
System.out.println("查詢用戶數(shù)據(jù)");
latch2.countDown(); // t2完成后打開閘門2
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread t3 = new Thread(() -> {
try {
latch2.await(); // 等待閘門2打開
System.out.println("生成報表");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 故意打亂啟動順序測試
t3.start();
t2.start();
t1.start();
}
}優(yōu)點:
- 靈活控制多個線程的依賴關系
- 支持一對多、多對多同步
- 不阻塞主線程
缺點:
- 需要創(chuàng)建多個CountDownLatch對象
- 計數(shù)器不可重置
適用場景:分階段任務,特別是多階段有復雜依賴關系的場景。
方法4:ReentrantLock與Condition - 精準控制的等待/通知機制
實現(xiàn)原理:
ReentrantLock配合Condition提供了比synchronized更靈活的線程通信機制。每個Condition對象實質(zhì)上是一個獨立的等待隊列,可以實現(xiàn)精確的線程喚醒控制。
import java.util.concurrent.locks.*;
public class ReentrantLockConditionExample {
// 可重入鎖
private static Lock lock = new ReentrantLock(true); // 公平鎖
// 三個條件變量
private static Condition condition1 = lock.newCondition();
private static Condition condition2 = lock.newCondition();
private static Condition condition3 = lock.newCondition();
// 狀態(tài)標志
private static volatile int flag = 1;
public static void main(String[] args) {
Thread t1 = new Thread(() -> task(1, 2, condition1, condition2));
Thread t2 = new Thread(() -> task(2, 3, condition2, condition3));
Thread t3 = new Thread(() -> task(3, 1, condition3, condition1));
t1.start();
t2.start();
t3.start();
// 模擬運行后停止
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.exit(0);
}
private static void task(int currentFlag, int nextFlag,
Condition waitCondition, Condition signalCondition) {
while (true) {
lock.lock();
try {
// 檢查是否輪到自己執(zhí)行
while (flag != currentFlag) {
waitCondition.await(); // 釋放鎖并等待
}
System.out.println("Thread " + currentFlag + " 正在執(zhí)行");
Thread.sleep(1000); // 模擬處理時間
// 更新狀態(tài)并喚醒下一個線程
flag = nextFlag;
signalCondition.signal();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
}
}關鍵點解析:
- 公平鎖:構(gòu)造
ReentrantLock時傳入true參數(shù)創(chuàng)建公平鎖,減少線程饑餓 - 條件變量:每個線程有自己專屬的
Condition,避免無效喚醒 - volatile變量:確保狀態(tài)標志的可見性
- await/signal:精確控制線程的等待和喚醒
優(yōu)點:
- 最精確的線程控制能力
- 支持公平性配置
- 可中斷的等待機制
- 支持多個等待條件
缺點:
- 實現(xiàn)復雜度高
- 需要手動管理鎖的獲取和釋放
- 容易遺漏unlock導致死鎖
適用場景:
- 需要精確控制線程執(zhí)行順序的復雜場景
- 多條件等待的線程協(xié)作
- 對公平性有要求的場景
方法5:Semaphore - 基于許可的同步控制
實現(xiàn)原理:
Semaphore通過維護一組許可(permits)來控制線程訪問。初始化時指定許可數(shù)量,線程通過acquire()獲取許可,通過release()釋放許可。
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
// 初始化信號量:t1可以直接運行,t2和t3需要等待
private static Semaphore s1 = new Semaphore(1);
private static Semaphore s2 = new Semaphore(0);
private static Semaphore s3 = new Semaphore(0);
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
s1.acquire(); // 獲取許可
System.out.println("線程1:數(shù)據(jù)加載");
Thread.sleep(1000);
s2.release(); // 釋放t2的許可
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread t2 = new Thread(() -> {
try {
s2.acquire();
System.out.println("線程2:數(shù)據(jù)處理");
Thread.sleep(1000);
s3.release();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread t3 = new Thread(() -> {
try {
s3.acquire();
System.out.println("線程3:結(jié)果保存");
Thread.sleep(1000);
s1.release(); // 循環(huán)執(zhí)行時可重新開始
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 啟動線程
t1.start();
t2.start();
t3.start();
// 允許程序運行一段時間后退出
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}進階用法:
- 公平模式:
new Semaphore(1, true) - 批量獲取:
acquire(int permits) - 非阻塞獲取:
tryAcquire() - 超時控制:
tryAcquire(long timeout, TimeUnit unit)
優(yōu)點:
- 靈活控制并發(fā)度
- 支持許可的申請和釋放
- 可實現(xiàn)資源池等復雜模式
缺點:
- 需要仔細設計許可數(shù)量
方法6:CompletableFuture - 函數(shù)式異步編程
實現(xiàn)原理:
CompletableFuture是Java 8引入的增強版Future,支持函數(shù)式編程風格的任務編排。通過thenRun、thenApply等方法鏈式組合多個異步任務。
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureExample {
public static void main(String[] args) {
// 創(chuàng)建異步任務鏈
CompletableFuture<Void> taskChain = CompletableFuture
.runAsync(() -> {
System.out.println("任務1:初始化系統(tǒng)");
sleep(1000);
})
.thenRunAsync(() -> {
System.out.println("任務2:加載配置");
sleep(1500);
})
.thenRunAsync(() -> {
System.out.println("任務3:啟動服務");
sleep(500);
})
.thenRunAsync(() -> {
System.out.println("任務4:運行監(jiān)控");
});
// 等待所有任務完成
try {
taskChain.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
private static void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}優(yōu)點:
- 聲明式編程風格
- 強大的任務組合能力
- 內(nèi)置異常處理機制
- 與現(xiàn)代Java特性完美集成
缺點:
- 學習曲線較陡峭
- 調(diào)試相對困難
- Java 8+才支持
適用場景:
- 現(xiàn)代Java應用開發(fā)
- 復雜的異步任務編排
- 需要組合多個異步結(jié)果的場景
- 響應式編程基礎
三、常見問題與解決方案
Q1:為什么await()要在while循環(huán)中調(diào)用?
A:這是為了防止"虛假喚醒"(spurious wakeup),即線程可能在沒有收到通知的情況下被喚醒。while循環(huán)會重新檢查條件,確保條件真正滿足。
Q2:如何避免死鎖?
A:遵循以下原則:
- 按固定順序獲取多個鎖
- 設置鎖超時時間
- 避免在鎖中調(diào)用外部方法
- 使用tryLock()替代lock()
以上就是Java中控制多線程順序執(zhí)行的六種實現(xiàn)方案的詳細內(nèi)容,更多關于Java控制多線程順序執(zhí)行的資料請關注腳本之家其它相關文章!
相關文章
SpringBoot后端實現(xiàn)小程序微信登錄功能實現(xiàn)
微信小程序登錄是開發(fā)者通過微信提供的身份驗證機制,獲取用戶唯一標識(openid)和會話密鑰(session_key)的過程,這篇文章給大家介紹SpringBoot后端實現(xiàn)小程序微信登錄功能實現(xiàn),感興趣的朋友跟隨小編一起看看吧2025-05-05
SpringBoot WebSocket實時監(jiān)控異常的詳細流程
最近做了一個需求,消防的設備巡檢,如果巡檢發(fā)現(xiàn)異常,通過手機端提交,后臺的實時監(jiān)控頁面實時獲取到該設備的信息及位置,然后安排員工去處理。這篇文章主要介紹了SpringBoot WebSocket實時監(jiān)控異常的全過程,感興趣的朋友一起看看吧2021-10-10
從零搭建SpringBoot+MyBatisPlus快速開發(fā)腳手架
這篇文章主要為大家介紹了從零搭建SpringBoot+MyBatisPlus快速開發(fā)腳手架示例教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06
使用Spring的FactoryBean創(chuàng)建和獲取Bean對象方式
這篇文章主要介紹了使用Spring的FactoryBean創(chuàng)建和獲取Bean對象方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-03-03
Activiti7與Spring以及Spring Boot整合開發(fā)
這篇文章主要介紹了Activiti7與Spring以及Spring Boot整合開發(fā),在Activiti中核心類的是ProcessEngine流程引擎,與Spring整合就是讓Spring來管理ProcessEngine,有感興趣的同學可以參考閱讀2023-03-03

