Java 線程創(chuàng)建方式全過程
1、繼承Thread類
特點(diǎn):
1、編程簡單直觀
直接在類中編寫線程邏輯
適合簡單的線程場景
2、受單繼承限制,不利于資源共享,耦合度高,不適合線程池
類代碼:
public class ThreadExtend extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("線程執(zhí)行:" + Thread.currentThread().getName());
}
}
}
主程序代碼:
public class main {
public static void main(String[] args) {
ThreadExtend thread1=new ThreadExtend();
thread1.start();
ThreadExtend thread2=new ThreadExtend();
thread2.start();
}
}
//結(jié)果
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-02、實(shí)現(xiàn)Runnable接口
特點(diǎn):
1、避免單繼承局限
Java 是單繼承語言,實(shí)現(xiàn) Runnable 后還可以繼承其他類
更靈活,符合面向?qū)ο蟮脑O(shè)計(jì)原則
2、適合資源共享
多個(gè)線程可以共享同一個(gè) Runnable 實(shí)例
適合多個(gè)線程處理同一資源的場景
3、便于線程池管理
線程池主要接受 Runnable/Callable 任務(wù)
更容易與現(xiàn)代并發(fā)工具集成
4、代碼解耦
將任務(wù)邏輯與線程控制分離
更符合"組合優(yōu)于繼承"的設(shè)計(jì)原則
類代碼:
//演示共享資源
public class ThreadRunnable implements Runnable{
private int nums=20;
@Override
public void run() {
while(nums>0) {
System.out.println("線程執(zhí)行:" + Thread.currentThread().getName());
nums--;
}
}
}主程序代碼:
public class main {
public static void main(String[] args) {
ThreadRunnable threadRunnable = new ThreadRunnable();
Thread t1=new Thread(threadRunnable,"線程1");
Thread t2=new Thread(threadRunnable,"線程2");
Thread t3=new Thread(threadRunnable,"線程3");
t1.start();
t2.start();
t3.start();
}
}
//示例結(jié)果出現(xiàn)了線程安全問題,輸出大于20個(gè),可以在while循環(huán)內(nèi)加鎖
線程執(zhí)行:線程3
線程執(zhí)行:線程3
線程執(zhí)行:線程3
線程執(zhí)行:線程3
線程執(zhí)行:線程3
線程執(zhí)行:線程3
線程執(zhí)行:線程3
線程執(zhí)行:線程3
線程執(zhí)行:線程3
線程執(zhí)行:線程3
線程執(zhí)行:線程3
線程執(zhí)行:線程3
線程執(zhí)行:線程3
線程執(zhí)行:線程3
線程執(zhí)行:線程3
線程執(zhí)行:線程3
線程執(zhí)行:線程3
線程執(zhí)行:線程2
線程執(zhí)行:線程2
線程執(zhí)行:線程2
線程執(zhí)行:線程1
線程執(zhí)行:線程33、實(shí)現(xiàn)Callable接口
特點(diǎn):
1、
需要重寫call()方法,有返回值,可以通過FutureTask獲取狀態(tài)。
FutureTask常用方法
(1)get()-阻塞獲取結(jié)果
String result = futureTask.get(); // 一直等待直到任務(wù)完成
(2)get(long timeout, TimeUnit unit) - 超時(shí)獲取
// 最多等待5秒,超時(shí)拋 TimeoutException String result = futureTask.get(5, TimeUnit.SECONDS);
(3)isDone() - 是否完成
boolean done = futureTask.isDone(); // true: 任務(wù)已完成(正常完成、異?;蛉∠? // false: 任務(wù)還在執(zhí)行中
(4)isCancelled() - 是否被取消
boolean cancelled = futureTask.isCancelled(); // true: 任務(wù)在正常完成前被取消 // false: 任務(wù)未被取消
(5)cancel(boolean mayInterruptIfRunning) - 取消任務(wù)
boolean cancelled = futureTask.cancel(false); // false: 不中斷正在運(yùn)行的任務(wù),只阻止未開始的任務(wù) // true: 嘗試中斷正在運(yùn)行的任務(wù) // 返回值: // true: 取消成功 // false: 取消失?。ㄈ蝿?wù)已完成或已被取消)
cancel(false)在線程還在線程池排隊(duì)沒運(yùn)行的時(shí)候能停下來
cancel(true)需要在run()代碼中判斷Thread.currentThread().isInterrupted(),檢測線程是否收到中斷信號來手動中斷?;蛘邎?zhí)行sleep()等響應(yīng)中斷的阻塞方法也會使得任務(wù)退出。
(6)run() - 執(zhí)行任務(wù)
futureTask.run(); // 通常在 Thread 中調(diào)用 // new Thread(futureTask).start(); 內(nèi)部會調(diào)用這個(gè)方法
Thread.start()調(diào)用第二次會直接報(bào)錯(cuò)拋出異常。
FutureTask.run()調(diào)用第二次不會報(bào)錯(cuò)但是會被靜默忽略不會真正執(zhí)行。
(7)runAndReset() - 執(zhí)行并重置
boolean reset = futureTask.runAndReset(); // 用于可重復(fù)執(zhí)行的任務(wù),執(zhí)行后重置狀態(tài)
這個(gè)方法可以重復(fù)執(zhí)行,但是不能通過get得到結(jié)果。
2、
可以拋出異常。
類代碼:
import java.util.concurrent.Callable;
public class ThreadCallable implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i < 10; i++) {
System.out.println("線程執(zhí)行:" + Thread.currentThread().getName());
}
return Thread.currentThread().getName()+"線程執(zhí)行完畢";
}
}主程序代碼:
public static void main(String[] args) {
FutureTask<String> futureTask1 = new FutureTask<>(new ThreadCallable());
Thread thread1 = new Thread(futureTask1);
thread1.start();
FutureTask<String> futureTask2 = new FutureTask<>(new ThreadCallable());
Thread thread2 = new Thread(futureTask2);
thread2.start();
try{
System.out.println(futureTask1.get());
}
catch(Exception e){
e.printStackTrace();
}
try{
System.out.println(futureTask2.get());
}
catch(Exception e){
e.printStackTrace();
}
}
//結(jié)果
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-1
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-0
線程執(zhí)行:Thread-0
Thread-0線程執(zhí)行完畢
Thread-1線程執(zhí)行完畢4、線程池
參數(shù):
public ThreadPoolExecutor(
int corePoolSize, // 1. 核心線程數(shù)
int maximumPoolSize, // 2. 最大線程數(shù)
long keepAliveTime, // 3. 空閑線程存活時(shí)間
TimeUnit unit, // 4. 時(shí)間單位
BlockingQueue<Runnable> workQueue, // 5. 任務(wù)阻塞隊(duì)列
ThreadFactory threadFactory, // 6. 線程工廠
RejectedExecutionHandler handler // 7. 拒絕策略
)corePoolSize(核心線程數(shù)):線程池中平時(shí)常駐的線程數(shù)量,即使空閑也不會被銷毀。
maximumPoolSize(最大線程數(shù)):線程池允許創(chuàng)建的最大線程數(shù)(核心線程 + 臨時(shí)線程)。
keepAliveTime(空閑存活時(shí)間):當(dāng)線程數(shù)超過核心線程數(shù)時(shí),多余的空閑線程在銷毀前等待新任務(wù)的最長時(shí)間。
unit(時(shí)間單位),為keepAliveTime的時(shí)間單位
workQueue(任務(wù)隊(duì)列):當(dāng)核心線程都在忙碌時(shí),新提交的任務(wù)會被放入這個(gè)隊(duì)列中等待。 threadFactory(線程工廠):用于創(chuàng)建新線程,通常用來給線程設(shè)置有業(yè)務(wù)意義的名稱,方便排查問題。
handler(拒絕策略):當(dāng)隊(duì)列滿了,且線程數(shù)達(dá)到了最大值時(shí),對新任務(wù)的兜底處理策略(如直接拋出異常、讓提交者自己執(zhí)行等)。
常用阻塞隊(duì)列:
ArrayBlockingQueue:
基于數(shù)組實(shí)現(xiàn)的有界阻塞隊(duì)列,初始化時(shí)必須指定容量。它采用 FIFO(先進(jìn)先出)原則,且內(nèi)部使用一把全局鎖來保證線程安全。
LinkedBlockingQueue
基于鏈表實(shí)現(xiàn)的阻塞隊(duì)列。它既可以是有界的,也可以是無界的(如果初始化時(shí)不指定容量,默認(rèn)容量為 Integer.MAX_VALUE)。它采用了讀寫分離的雙鎖機(jī)制,在高并發(fā)下吞吐量ArrayBlockingQueue 更高。注意無界時(shí)候容易棧溢出。
SynchronousQueue(零容量、手遞手)
核心特點(diǎn):一個(gè)不存儲任何元素的特殊隊(duì)列(容量為 0)。它的每次放入操作必須等待一個(gè)對應(yīng)的取出操作
生產(chǎn)建議:通常會配合較大的 maximumPoolSize,因?yàn)闆]有排隊(duì)緩沖,新任務(wù)來了必須立刻創(chuàng)建線程接手。
PriorityBlockingQueue
一個(gè)支持優(yōu)先級的無界阻塞隊(duì)列。任務(wù)不再是先進(jìn)先出,而是根據(jù)自定義的比較器或自然順序來決定執(zhí)行順序。
延遲隊(duì)列:
使用 JDK 自帶的 ScheduledThreadPoolExecutor,內(nèi)部配置了DelayedWorkQueue,這個(gè)隊(duì)列和ThreadPoolExecutor不適配。
常用拒絕策略:
AbortPolicy(默認(rèn)策略)
直接拋出一個(gè) RejectedExecutionException 運(yùn)行時(shí)異常,阻止任務(wù)提交。
CallerRunsPolicy(調(diào)用者運(yùn)行策略)
不拋出異常,也不丟棄任務(wù),而是將當(dāng)前被拒絕的任務(wù)退回到提交該任務(wù)的線程(即調(diào)用者線程)中去執(zhí)行。
適合允許延遲,任務(wù)不可丟失的的后臺任務(wù)
原理:因?yàn)檎{(diào)用者線程需要親自去執(zhí)行被拒絕的任務(wù),這會占用調(diào)用者的時(shí)間,從而自然地降低新任務(wù)提交的速度,讓系統(tǒng)在高負(fù)載下實(shí)現(xiàn)平滑降級。
DiscardPolicy(丟棄策略)
靜默地直接丟棄當(dāng)前被拒絕的任務(wù),不拋出任何異常,也不做任何通知。
DiscardOldestPolicy(丟棄最舊任務(wù)策略)
丟棄任務(wù)隊(duì)列中等待時(shí)間最長(即隊(duì)首)的那個(gè)任務(wù),然后嘗試重新提交當(dāng)前這個(gè)最新的任務(wù)。
特點(diǎn):
1、降低資源消耗,提高響應(yīng)速度(線程復(fù)用)
傳統(tǒng)方式:線程的創(chuàng)建和銷毀是非常消耗系統(tǒng)資源的操作,需要頻繁進(jìn)行系統(tǒng)調(diào)用和上下文切換。
線程池:通過預(yù)先創(chuàng)建一批線程并長期存活,任務(wù)執(zhí)行完畢后線程不會被銷毀,而是歸還到池中等待下一個(gè)任務(wù)。這種線程復(fù)用機(jī)制,極大地降低了頻繁創(chuàng)建和銷毀線程帶來的 CPU 和內(nèi)存開銷。
2、增強(qiáng)系統(tǒng)的穩(wěn)定性與可控性(避免資源耗盡)
傳統(tǒng)方式:如果無限制地手動創(chuàng)建線程,會迅速耗盡系統(tǒng)的內(nèi)存和 CPU 資源,極易導(dǎo)致系統(tǒng)卡頓甚至發(fā)生內(nèi)存溢出崩潰。
線程池:可以對線程的最大并發(fā)數(shù)進(jìn)行嚴(yán)格控制。當(dāng)任務(wù)過多時(shí),多余的任務(wù)會被放入隊(duì)列等待,或者根據(jù)預(yù)設(shè)的拒絕策略進(jìn)行處理,從而有效防止資源過載,保障系統(tǒng)的穩(wěn)定性。
3、統(tǒng)一管理與功能豐富
傳統(tǒng)方式:手動創(chuàng)建的線程分散且獨(dú)立,開發(fā)者需要自己處理線程的生命周期、同步、優(yōu)先級等,管理難度大且容易出錯(cuò)。
線程池:提供了統(tǒng)一的線程調(diào)度、生命周期管理、狀態(tài)監(jiān)控以及定時(shí)/周期性任務(wù)執(zhí)行等高級功能,大大簡化了并發(fā)編程的復(fù)雜度。
類代碼:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolUtil {
public static ThreadPoolExecutor getThreadPoolExecutor()
{
return new ThreadPoolExecutor(
5,
10,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
}
}
主程序代碼:
import java.util.concurrent.FutureTask;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class main {
public static void main(String[] args) {
ThreadPoolExecutor executor = ThreadPoolUtil.getThreadPoolExecutor();
try{
for(int i=1;i<=20;i++)
{
int taskId=i;
executor.execute(()->{
System.out.println("任務(wù)"+taskId+"正在被"+Thread.currentThread().getName()+"執(zhí)行");
try{
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
catch (Exception e)
{
e.printStackTrace();
}
finally{
executor.shutdown();//不再接受新的任務(wù),線程池會等待所有任務(wù)執(zhí)行完畢,再關(guān)閉
try {
if(!executor.awaitTermination(60, TimeUnit.SECONDS)){//阻塞等待60秒線程池所有任務(wù)還沒有完成返回false
executor.shutdownNow();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
//運(yùn)行結(jié)果
任務(wù)3正在被pool-1-thread-4執(zhí)行
任務(wù)4正在被pool-1-thread-5執(zhí)行
任務(wù)2正在被pool-1-thread-3執(zhí)行
任務(wù)1正在被pool-1-thread-2執(zhí)行
任務(wù)0正在被pool-1-thread-1執(zhí)行
任務(wù)5正在被pool-1-thread-4執(zhí)行
任務(wù)6正在被pool-1-thread-5執(zhí)行
任務(wù)7正在被pool-1-thread-2執(zhí)行
任務(wù)9正在被pool-1-thread-1執(zhí)行
任務(wù)8正在被pool-1-thread-3執(zhí)行
任務(wù)10正在被pool-1-thread-1執(zhí)行
任務(wù)11正在被pool-1-thread-3執(zhí)行
任務(wù)12正在被pool-1-thread-2執(zhí)行
任務(wù)13正在被pool-1-thread-5執(zhí)行
任務(wù)14正在被pool-1-thread-4執(zhí)行
任務(wù)15正在被pool-1-thread-1執(zhí)行
任務(wù)16正在被pool-1-thread-3執(zhí)行
任務(wù)17正在被pool-1-thread-4執(zhí)行
任務(wù)18正在被pool-1-thread-5執(zhí)行
任務(wù)19正在被pool-1-thread-2執(zhí)行
示例補(bǔ)充:
示例代碼中核心線程數(shù)為5個(gè),因?yàn)?0個(gè)任務(wù)并沒有超過核心線程數(shù)+隊(duì)列數(shù),所以不會啟動擴(kuò)容創(chuàng)建新線程,因?yàn)榫€程運(yùn)行中睡眠一秒,能看到輸出情況是每一秒輸出五個(gè)。
5、虛擬線程
特點(diǎn):
1、資源消耗:極致的輕量
傳統(tǒng)線程:每個(gè)線程都有一個(gè)固定的、較大的棧內(nèi)存(默認(rèn) 1MB 左右),且由操作系統(tǒng)管理。如果你嘗試創(chuàng)建幾十萬個(gè)傳統(tǒng)線程,系統(tǒng)內(nèi)存會瞬間耗盡。
虛擬線程:
它的??臻g不是連續(xù)分配的,而是按需動態(tài)伸縮的(初始只有幾 KB)。你可以輕松在一臺普通電腦上創(chuàng)建數(shù)百萬個(gè)虛擬線程,而不會導(dǎo)致內(nèi)存溢出。
2、虛擬現(xiàn)場被設(shè)置為強(qiáng)制后臺線程,哪怕繼承了主線程,JVM也不會等待。
3、應(yīng)用場景:
適合:高吞吐量的 Web 服務(wù)、微服務(wù)調(diào)用、數(shù)據(jù)庫訪問。
不適合:CPU 密集型任務(wù)(如復(fù)雜的數(shù)學(xué)計(jì)算、圖像處理)。
原因:虛擬線程的優(yōu)勢在于 I/O 阻塞時(shí)的切換。如果是純計(jì)算任務(wù),它不會阻塞,會一直占用載體線程,此時(shí)它就退化成了普通線程,且因?yàn)檎{(diào)度層多了一層,反而可能略慢。對于 CPU 密集型任務(wù),還是應(yīng)該使用傳統(tǒng)的線程池。
創(chuàng)建虛擬線程:
Thread.Builder builder = Thread.ofVirtual().name("worker-", 0);
for (int i = 0; i < 3; i++) {
int taskId = i;
builder.start(() -> {
System.out.println("[線程3] 任務(wù) " + taskId + " 在 " + Thread.currentThread().getName() + " 中執(zhí)行");
});
}
// 主線程等待1秒,讓虛擬線程有時(shí)間執(zhí)行
Thread.sleep(1000);
//結(jié)果
[線程3] 任務(wù) 2 在 worker-2 中執(zhí)行
[線程3] 任務(wù) 0 在 worker-0 中執(zhí)行
[線程3] 任務(wù) 1 在 worker-1 中執(zhí)行
//或者更簡單的方法
//使用 Thread.startVirtualThread()
Thread vThread1 = Thread.startVirtualThread(() -> {
System.out.println("[線程1] 虛擬線程名稱: " + Thread.currentThread().getName());
System.out.println("[線程1] 是否是虛擬線程: " + Thread.currentThread().isVirtual());
});
vThread1.join();創(chuàng)建虛擬線程池:
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
// 提交多個(gè)任務(wù)
for (int i = 1; i <= 5; i++) {
int taskId = i;
executor.submit(() -> {
System.out.println("[線程池] 任務(wù) " + taskId + " 開始執(zhí)行 - " + Thread.currentThread().getName());
// 模擬 I/O 操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("[線程池] 任務(wù) " + taskId + " 完成執(zhí)行");
return "結(jié)果-" + taskId;
});
}
// 等待所有任務(wù)完成
executor.shutdown();
executor.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("[線程池] 所有任務(wù)已完成\n");
//結(jié)果
[線程池] 任務(wù) 3 開始執(zhí)行 -
[線程池] 任務(wù) 2 開始執(zhí)行 -
[線程池] 任務(wù) 5 開始執(zhí)行 -
[線程池] 任務(wù) 1 開始執(zhí)行 -
[線程池] 任務(wù) 4 開始執(zhí)行 -
[線程池] 任務(wù) 1 完成執(zhí)行
[線程池] 任務(wù) 5 完成執(zhí)行
[線程池] 任務(wù) 4 完成執(zhí)行
[線程池] 任務(wù) 3 完成執(zhí)行
[線程池] 任務(wù) 2 完成執(zhí)行
[線程池] 所有任務(wù)已完成定時(shí)任務(wù)調(diào)度線程
(1)Timer,TimerTask
原理
使用 Timer 和 TimerTask 創(chuàng)建線程,并不是一種完全獨(dú)立、全新的底層創(chuàng)建線程的方法。它本質(zhì)上是基于 Java 最基礎(chǔ)的實(shí)現(xiàn) Runnable 接口這一方式,并在此基礎(chǔ)上進(jìn)行了更高層次的封裝,專門用于實(shí)現(xiàn)定時(shí)或周期性任務(wù)。
有以下缺點(diǎn):
單線程隱患:一個(gè) Timer 只有一個(gè)工作線程。如果其中一個(gè) TimerTask 執(zhí)行時(shí)間過長,會延遲其他所有任務(wù)的執(zhí)行;更嚴(yán)重的是,如果某個(gè)任務(wù)拋出了未捕獲的異常,整個(gè) Timer 的工作線程就會直接掛掉,導(dǎo)致后續(xù)所有任務(wù)都無法再執(zhí)行。
依賴系統(tǒng)時(shí)間:Timer 是基于系統(tǒng)的絕對時(shí)間來調(diào)度的,如果操作系統(tǒng)的系統(tǒng)時(shí)間被修改,定時(shí)任務(wù)的執(zhí)行就會變得不準(zhǔn)確。
所以現(xiàn)在不常用。
(2)ScheduledThreadPoolExecutor
特點(diǎn):
1. 繼承自 ThreadPoolExecutor,這意味著它天然擁有了標(biāo)準(zhǔn)線程池的所有基礎(chǔ)能力,比如線程的復(fù)用、并發(fā)控制、線程生命周期管理等。
2、核心任務(wù)隊(duì)列:DelayedWorkQueue
底層結(jié)構(gòu):這是一個(gè)基于最小堆(Min-Heap)數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的優(yōu)先級隊(duì)列。
核心作用:它會把所有提交的任務(wù),按照“距離執(zhí)行時(shí)間”的長短進(jìn)行排序。距離下次執(zhí)行時(shí)間最近的任務(wù),永遠(yuǎn)會排在隊(duì)列的最前面(堆頂)。工作線程每次只需要盯著堆頂?shù)娜蝿?wù),如果時(shí)間沒到就繼續(xù)等待,從而實(shí)現(xiàn)了精準(zhǔn)的定時(shí)調(diào)度。
3、核心任務(wù)封裝:ScheduledFutureTask
當(dāng)你提交一個(gè)定時(shí)任務(wù)時(shí),ScheduledThreadPoolExecutor 不會直接把你的任務(wù)扔進(jìn)隊(duì)列,而是會先把它包裝成一個(gè) ScheduledFutureTask 對象。
核心作用:這個(gè)包裝類里不僅包含了你的業(yè)務(wù)邏輯,還額外記錄了任務(wù)的觸發(fā)時(shí)間和周期。
周期性原理:對于周期性任務(wù),這個(gè)包裝類在執(zhí)行完一次任務(wù)后,會自動計(jì)算出下一次應(yīng)該執(zhí)行的時(shí)間,然后把自己重新塞回 DelayedWorkQueue 中排隊(duì)。這就形成了一個(gè)“執(zhí)行 -> 計(jì)算下次時(shí)間 -> 重新入隊(duì) -> 等待執(zhí)行”的完美閉環(huán)。
示例代碼:
// 創(chuàng)建定時(shí)任務(wù)線程池
ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(3);
System.out.println("程序啟動時(shí)間:" + System.currentTimeMillis());
// 1. 延遲5秒執(zhí)行一次
scheduler.schedule(() -> {
System.out.println("[延遲任務(wù)] 執(zhí)行時(shí)間:" + System.currentTimeMillis());
}, 5, TimeUnit.SECONDS);
// 2. 固定頻率執(zhí)行:初始延遲2秒,每3秒執(zhí)行一次
scheduler.scheduleAtFixedRate(() -> {
System.out.println("[固定頻率] 執(zhí)行時(shí)間:" + System.currentTimeMillis());
}, 2, 3, TimeUnit.SECONDS);
// 3. 固定延遲執(zhí)行:初始延遲1秒,每次執(zhí)行完延遲2秒
scheduler.scheduleWithFixedDelay(() -> {
System.out.println("[固定延遲] 執(zhí)行時(shí)間:" + System.currentTimeMillis());
try {
Thread.sleep(500); // 模擬任務(wù)執(zhí)行
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, 1, 2, TimeUnit.SECONDS);
// 主線程等待15秒后關(guān)閉線程池
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 關(guān)閉線程池
scheduler.shutdown();示例補(bǔ)充:
scheduleWithFixedDelay:保證任務(wù)之間有固定的"休息時(shí)間",適合需要控制執(zhí)行頻率、避免系統(tǒng)過載的場景
scheduleAtFixedRate:保證任務(wù)按固定時(shí)間點(diǎn)執(zhí)行,適合對時(shí)間精度要求高的場景
在使用scheduleAtFixedRate的時(shí)候如果執(zhí)行時(shí)間大于設(shè)置周期,任務(wù)會一個(gè)接一個(gè)立即執(zhí)行,沒有等待時(shí)間,長時(shí)間下去會導(dǎo)致任務(wù)堆積,內(nèi)存溢出,線程饑餓等問題。
--------------------------------------------------------------------------------------------------------------------
到這里就結(jié)束嘍??????
到此這篇關(guān)于Java 線程創(chuàng)建方式全過程的文章就介紹到這了,更多相關(guān)Java 線程創(chuàng)建方式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決try-catch捕獲異常信息后Spring事務(wù)失效的問題
這篇文章主要介紹了解決try-catch捕獲異常信息后Spring事務(wù)失效的問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
maven打包web項(xiàng)目時(shí)同時(shí)打包為war和jar文件的方法
本篇文章主要介紹了maven打包web項(xiàng)目時(shí)同時(shí)打包為war和jar文件的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10
EasyExcel與Apache POI版本沖突導(dǎo)致`NoSuchMethodError`異常的解決方法
本文介紹了EasyExcel與ApachePOI版本沖突導(dǎo)致`NoSuchMethodError`異常的原因,并提供了兩種解決方案:排除EasyExcel自帶的POI并手動指定統(tǒng)一版本,或者移除自己引入的POI,同時(shí),也強(qiáng)調(diào)了在使用EasyExcel時(shí)避免單獨(dú)引入POI的重要性,需要的朋友可以參考下2025-11-11
MyBatis實(shí)現(xiàn)動態(tài)SQL更新的代碼示例
本文博小編將帶領(lǐng)大家學(xué)習(xí)如何利用 MyBatis 攔截器機(jī)制來優(yōu)雅的實(shí)現(xiàn)這個(gè)需求,文中通過代碼示例介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2023-07-07
IDEA運(yùn)行java程序時(shí)總彈出提示配置Edit Configurations問題
更新IDEA后,運(yùn)行Java程序時(shí)可能需要手動配置JDK版本,通過統(tǒng)一設(shè)置默認(rèn)JDK可以解決每次彈出EditConfigurations的問題,此操作可以簡化開發(fā)流程,提高效率2024-09-09

