偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

面試問你:為什么有Timer,還會(huì)開發(fā)@Scheduled?

開發(fā) 前端
如果你的項(xiàng)目中定時(shí)任務(wù)非常多(比如上百個(gè)),或者需要?jiǎng)討B(tài)添加/刪除任務(wù)、監(jiān)控任務(wù)執(zhí)行狀態(tài),??@Scheduled??可能不夠用了,這時(shí)可以考慮更專業(yè)的分布式定時(shí)任務(wù)框架,比如XXL-Job、Elastic-Job(不過這是后話了,日常開發(fā)中??@Scheduled??已經(jīng)能覆蓋80%以上的場(chǎng)景)。

誰才是定時(shí)任務(wù)的“王者”?

作為Java開發(fā)者,定時(shí)任務(wù)是日常開發(fā)中繞不開的需求——比如凌晨3點(diǎn)同步數(shù)據(jù)、每小時(shí)生成報(bào)表、每天定點(diǎn)推送通知。

提到定時(shí)任務(wù),很多人首先會(huì)想到JDK自帶的Timer,但在Spring項(xiàng)目里,大家更習(xí)慣用@Scheduled注解。

明明JDK已經(jīng)提供了定時(shí)能力,Spring為什么還要專門開發(fā)@Scheduled?

今天我們就從原理、用法、局限性三個(gè)維度拆解,搞懂這兩者的“恩怨情仇”。

JDK Timer與Spring @Scheduled:定時(shí)任務(wù)的雙雄

在Java生態(tài)中,JDK Timer是“元老級(jí)”的定時(shí)工具,從JDK 1.3開始就存在,是很多開發(fā)者接觸的第一個(gè)定時(shí)方案;

而Spring的@Scheduled則是“后起之秀”,隨著Spring框架的普及,逐漸成為企業(yè)級(jí)開發(fā)的首選。

兩者的核心目標(biāo)一致:在指定時(shí)間執(zhí)行任務(wù),但實(shí)現(xiàn)邏輯、功能特性、適用場(chǎng)景卻天差地別。搞懂它們的差異,不僅能幫你在項(xiàng)目中選對(duì)工具,更能理解“框架為何要封裝原生API”的設(shè)計(jì)思路。

JDK Timer:Java定時(shí)任務(wù)的“基石”,但不夠靈活

先從大家熟悉的JDK Timer說起。它的設(shè)計(jì)很簡(jiǎn)單,核心是兩個(gè)類:java.util.Timerjava.util.TimerTask。

1. 基本概念與原理

  • TimerTask:抽象類,代表“要執(zhí)行的任務(wù)”,開發(fā)者需要繼承它并實(shí)現(xiàn)run()方法,把具體邏輯寫在里面。
  • Timer:調(diào)度器,負(fù)責(zé)“安排任務(wù)執(zhí)行”。它內(nèi)部維護(hù)了一個(gè)單線程(TimerThread),這個(gè)線程會(huì)不斷從任務(wù)隊(duì)列(TaskQueue)中取出任務(wù),判斷是否到執(zhí)行時(shí)間,到點(diǎn)就執(zhí)行TimerTaskrun()方法。
  • 調(diào)度方式:基于“絕對(duì)時(shí)間”(System.currentTimeMillis()),比如“延遲1000ms執(zhí)行”“每天0點(diǎn)執(zhí)行”,本質(zhì)都是計(jì)算出具體的時(shí)間戳,再由TimerThread輪詢判斷。

2. 使用示例:簡(jiǎn)單但“夠用”

Timer的用法很直觀,幾行代碼就能實(shí)現(xiàn)定時(shí)任務(wù),適合簡(jiǎn)單場(chǎng)景。

場(chǎng)景1:延遲3秒后執(zhí)行一次任務(wù)

import java.util.Timer;
import java.util.TimerTask;

publicclass TimerDemo {
    public static void main(String[] args) {
        // 1. 創(chuàng)建Timer調(diào)度器
        Timer timer = new Timer();
        
        // 2. 創(chuàng)建TimerTask任務(wù)
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                System.out.println("延遲3秒后執(zhí)行:" + System.currentTimeMillis());
                // 任務(wù)執(zhí)行完后關(guān)閉Timer(避免線程一直運(yùn)行)
                timer.cancel();
            }
        };
        
        // 3. 安排任務(wù):延遲3000ms執(zhí)行
        timer.schedule(task, 3000);
    }
}

場(chǎng)景2:延遲1秒后,每隔2秒執(zhí)行一次任務(wù)

// 安排任務(wù):延遲1000ms,之后每隔2000ms執(zhí)行一次
timer.schedule(task, 1000, 2000);

3. 局限性:?jiǎn)尉€程+敏感時(shí)間,坑不少

雖然Timer能實(shí)現(xiàn)基礎(chǔ)定時(shí),但在復(fù)雜業(yè)務(wù)場(chǎng)景下,它的缺點(diǎn)會(huì)被無限放大,甚至導(dǎo)致線上問題:

  • 單線程執(zhí)行,任務(wù)相互阻塞:Timer內(nèi)部只有一個(gè)線程,如果一個(gè)任務(wù)執(zhí)行時(shí)間過長(zhǎng)(比如本應(yīng)2秒的任務(wù)跑了10秒),后面所有任務(wù)都會(huì)被延遲。比如任務(wù)A每隔2秒執(zhí)行,任務(wù)B在任務(wù)A之后1秒執(zhí)行,若任務(wù)A卡了10秒,任務(wù)B會(huì)直接被“插隊(duì)”,直到A執(zhí)行完才會(huì)跑。
  • 對(duì)系統(tǒng)時(shí)間敏感,可能導(dǎo)致任務(wù)失效:Timer基于絕對(duì)時(shí)間調(diào)度,如果系統(tǒng)時(shí)間被修改(比如往回調(diào)1小時(shí)),原本該執(zhí)行的任務(wù)可能會(huì)“卡住”,甚至永遠(yuǎn)不執(zhí)行。比如你設(shè)置“每天0點(diǎn)執(zhí)行”,若系統(tǒng)時(shí)間從0點(diǎn)1分調(diào)回23點(diǎn)59分,這個(gè)任務(wù)會(huì)再次等待1分鐘才執(zhí)行,不符合預(yù)期。
  • 任務(wù)異常會(huì)導(dǎo)致整個(gè)Timer崩潰:如果一個(gè)TimerTaskrun()方法拋出未捕獲異常,TimerThread會(huì)直接終止,后續(xù)所有任務(wù)都不會(huì)再執(zhí)行。比如任務(wù)A拋了空指針,任務(wù)B、C即使到了時(shí)間也不會(huì)運(yùn)行,排查起來很麻煩。

定時(shí)任務(wù)的“利器”,專為企業(yè)級(jí)開發(fā)設(shè)計(jì)

Spring框架的核心思想是“簡(jiǎn)化開發(fā)”,@Scheduled就是對(duì)定時(shí)任務(wù)的封裝——它解決了Timer的所有痛點(diǎn),還提供了更靈活的配置和更穩(wěn)定的執(zhí)行機(jī)制。

1. 功能概述:注解化配置,開箱即用

@Scheduled是Spring的一個(gè)注解,只要在Spring管理的Bean的方法上添加該注解,就能將方法變成定時(shí)任務(wù)。

它不需要手動(dòng)創(chuàng)建調(diào)度器、任務(wù)隊(duì)列,Spring會(huì)自動(dòng)掃描、初始化、管理任務(wù),開發(fā)者只需關(guān)注“任務(wù)邏輯”和“執(zhí)行時(shí)間”。

2. 使用方法與配置:3步搞定,靈活度拉滿

步驟1:開啟定時(shí)任務(wù)支持

在Spring Boot啟動(dòng)類(或Spring配置類)上添加@EnableScheduling注解,告訴Spring“要啟用定時(shí)任務(wù)功能”:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling // 開啟定時(shí)任務(wù)
public class ScheduledDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(ScheduledDemoApplication.class, args);
    }
}

步驟2:寫定時(shí)任務(wù)方法

在Bean的方法上添加@Scheduled,并配置執(zhí)行時(shí)間(支持3種常用配置):

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component// 必須是Spring Bean
publicclass MyScheduledTask {

    // 1. fixedRate:每隔5秒執(zhí)行一次(以上次任務(wù)開始時(shí)間計(jì)算)
    @Scheduled(fixedRate = 5000)
    public void taskWithFixedRate() {
        System.out.println("fixedRate任務(wù)執(zhí)行:" + System.currentTimeMillis());
        // 假設(shè)任務(wù)執(zhí)行需要2秒
        try { Thread.sleep(2000); } catch (InterruptedException e) {}
    }

    // 2. fixedDelay:每隔5秒執(zhí)行一次(以上次任務(wù)結(jié)束時(shí)間計(jì)算)
    @Scheduled(fixedDelay = 5000)
    public void taskWithFixedDelay() {
        System.out.println("fixedDelay任務(wù)執(zhí)行:" + System.currentTimeMillis());
        try { Thread.sleep(2000); } catch (InterruptedException e) {}
    }

    // 3. cron表達(dá)式:每天0點(diǎn)30分執(zhí)行(最靈活的配置)
    @Scheduled(cron = "0 30 0 * * ?")
    public void taskWithCron() {
        System.out.println("cron任務(wù)執(zhí)行:" + System.currentTimeMillis());
    }
}

關(guān)鍵配置說明

配置項(xiàng)

作用

示例

fixedRate

固定頻率執(zhí)行,以上次任務(wù)開始時(shí)間

fixedRate=5000(5秒)

fixedDelay

固定延遲執(zhí)行,以上次任務(wù)結(jié)束時(shí)間

fixedDelay=5000(5秒)

cron

復(fù)雜時(shí)間配置(支持秒、分、時(shí)、日等)

0 30 0 * * ?(每天0:30)

其中cron表達(dá)式是最強(qiáng)大的,比如“每周一到周五下午3點(diǎn)15分”可以寫為0 15 15 ? * MON-FRI,幾乎能滿足所有業(yè)務(wù)場(chǎng)景。

3. 底層原理:多線程+解耦,穩(wěn)定性拉滿

@Scheduled的底層比Timer復(fù)雜,但核心是“解耦”和“多線程”,我們拆解關(guān)鍵組件:

  1. ScheduledAnnotationBeanPostProcessor:Spring的后置處理器,在Bean初始化時(shí)掃描帶有@Scheduled的方法,將其封裝成ScheduledTask對(duì)象,交給調(diào)度器管理。
  2. TaskScheduler:Spring的調(diào)度器接口(類似Timer的角色),默認(rèn)實(shí)現(xiàn)是ThreadPoolTaskScheduler——它內(nèi)部維護(hù)了一個(gè)線程池(默認(rèn)核心線程數(shù)是1,但可以配置成多線程)。
  3. 任務(wù)執(zhí)行機(jī)制:每個(gè)ScheduledTask會(huì)被提交到線程池執(zhí)行,即使一個(gè)任務(wù)拋了異常,也只會(huì)影響當(dāng)前線程,其他任務(wù)正常執(zhí)行(Spring會(huì)捕獲異常并打印日志,不會(huì)導(dǎo)致整個(gè)調(diào)度器崩潰)。

兩者深度對(duì)比:選對(duì)工具,少踩坑

為了更直觀地看出差異,我們從4個(gè)核心維度做對(duì)比:

對(duì)比維度

JDK Timer

Spring @Scheduled

線程模型

單線程(TimerThread),任務(wù)串行執(zhí)行,相互阻塞

基于線程池(可配置多線程),任務(wù)并行執(zhí)行,互不影響

時(shí)間控制

僅支持延遲、固定周期(基于絕對(duì)時(shí)間),不支持復(fù)雜時(shí)間

支持fixedRate、fixedDelay、cron表達(dá)式,復(fù)雜時(shí)間配置靈活

異常處理

任務(wù)拋未捕獲異常會(huì)導(dǎo)致TimerThread終止,所有任務(wù)失效

異常被線程池捕獲并日志記錄,單個(gè)任務(wù)異常不影響其他任務(wù)

配置與集成

需手動(dòng)創(chuàng)建Timer、TimerTask,無框架集成能力

注解化配置,自動(dòng)掃描,與Spring生態(tài)無縫集成(如依賴注入)

實(shí)際應(yīng)用場(chǎng)景選擇:不是“誰更好”,而是“誰更合適”

雖然@Scheduled優(yōu)勢(shì)明顯,但也不是所有場(chǎng)景都要用它,具體看需求:

選JDK Timer的場(chǎng)景:

  • 簡(jiǎn)單的獨(dú)立Java程序(非Spring項(xiàng)目),比如一個(gè)小工具需要定時(shí)執(zhí)行任務(wù)。
  • 任務(wù)量少、執(zhí)行時(shí)間短,無復(fù)雜時(shí)間需求(比如每隔10秒打印一次日志)。
  • 不依賴任何框架,追求“輕量”(不需要引入Spring依賴)。

選Spring @Scheduled的場(chǎng)景:

  • Spring Boot/Spring項(xiàng)目(企業(yè)級(jí)開發(fā)的主流場(chǎng)景)。
  • 任務(wù)需要并行執(zhí)行,或存在執(zhí)行時(shí)間較長(zhǎng)的任務(wù)(避免阻塞)。
  • 需要復(fù)雜的時(shí)間配置(比如每月最后一天23點(diǎn)執(zhí)行)。
  • 對(duì)任務(wù)穩(wěn)定性要求高(避免單個(gè)任務(wù)異常導(dǎo)致整體崩潰)。

“工具選擇”看框架設(shè)計(jì)思路

看到這里,你應(yīng)該明白“Spring為什么要開發(fā)@Scheduled”了——不是JDK Timer不好,而是它無法滿足企業(yè)級(jí)開發(fā)的“穩(wěn)定性、靈活性、集成性”需求。

Spring通過封裝,解決了原生API的痛點(diǎn),讓開發(fā)者能更專注于業(yè)務(wù)邏輯,而不是“如何管理調(diào)度器、處理異常、配置線程”。

最后給大家一個(gè)小拓展

如果你的項(xiàng)目中定時(shí)任務(wù)非常多(比如上百個(gè)),或者需要?jiǎng)討B(tài)添加/刪除任務(wù)、監(jiān)控任務(wù)執(zhí)行狀態(tài),@Scheduled可能不夠用了,這時(shí)可以考慮更專業(yè)的分布式定時(shí)任務(wù)框架,比如XXL-Job、Elastic-Job(不過這是后話了,日常開發(fā)中@Scheduled已經(jīng)能覆蓋80%以上的場(chǎng)景)。

責(zé)任編輯:武曉燕 來源: Java面試教程
相關(guān)推薦

2019-10-18 14:54:04

Kafka寫入磁盤

2022-01-14 08:12:39

瀏覽器面試問http

2018-04-12 17:29:03

軟件開發(fā)開發(fā)架構(gòu)

2017-02-16 20:05:36

軟件開發(fā)

2025-09-02 03:00:00

HTTPS敏感數(shù)據(jù)加密

2012-06-18 15:05:54

開發(fā)

2021-12-20 10:30:33

forforEach前端

2019-04-15 14:40:46

消息隊(duì)列Java編程

2025-04-09 00:00:55

2012-06-18 09:49:03

開發(fā)人員程序員

2017-05-31 08:45:03

2020-06-22 11:50:38

TCPIP協(xié)議

2009-03-03 09:33:13

面試ORACLE

2022-12-09 14:34:40

程序員工資離職

2023-08-29 07:46:08

Redis數(shù)據(jù)ReHash

2022-07-27 07:36:01

TCP可靠性

2025-03-04 08:06:17

2023-09-13 08:37:56

程序員面試catch

2013-05-29 10:47:50

Android開發(fā)Java多線程java面試題

2015-02-03 03:05:02

LinuxLinux命令
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)