最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中實(shí)現(xiàn)定時(shí)任務(wù)執(zhí)行的幾種常見方式總結(jié)

 更新時(shí)間:2025年09月02日 10:22:06   作者:inrgihc  
幾乎在所有的項(xiàng)目中,定時(shí)任務(wù)的使用都是不可或缺的,如果使用不當(dāng)甚至?xí)斐少Y損,這篇文章主要總結(jié)介紹了Java中實(shí)現(xiàn)定時(shí)任務(wù)執(zhí)行的幾種常見方式,需要的朋友可以參考下

一、概述

關(guān)于在Java項(xiàng)目中實(shí)現(xiàn)定時(shí)任務(wù)執(zhí)行的幾種方式總結(jié)如下:

單進(jìn)程實(shí)現(xiàn)定時(shí)任務(wù)主要有以下幾種方式:

1. Timer 和 TimerTask(JDK 原生)

2. ScheduledExecutorService(JDK 1.5+,推薦)

3. Spring Task(Spring 框架支持)

4. Quartz(企業(yè)級任務(wù)調(diào)度框架)

5. DelayQueue 阻塞隊(duì)列

分布式定時(shí)任務(wù)

1、使用Redis來實(shí)現(xiàn)定時(shí)任務(wù)

2、使用xxl-job實(shí)現(xiàn)定時(shí)任務(wù)

二、詳情

1. Timer 和 TimerTask(JDK 原生)

Java中的Timer類是一個(gè)定時(shí)調(diào)度器,用于在指定的時(shí)間點(diǎn)執(zhí)行任務(wù)。Timer 類用于實(shí)現(xiàn)定時(shí)任務(wù),最大的好處就是他的實(shí)現(xiàn)非常簡單,特別的輕量級,因?yàn)樗荍ava內(nèi)置的,所以只需要簡單調(diào)用就行了。

public class MyTimerTask {

    public static void main(String[] args) {
        // 定義一個(gè)任務(wù)
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("Run timerTask:" + new Date());
            }
        };
        // 計(jì)時(shí)器
        Timer timer = new Timer();
        // 添加執(zhí)行任務(wù)(延遲 1s 執(zhí)行,每 3s 執(zhí)行一次)
        timer.schedule(timerTask, 1000, 3000);
    }
}
優(yōu)點(diǎn)缺點(diǎn)
  • 簡單易用,適合單線程任務(wù)調(diào)度。
  • 基于絕對時(shí)間(Date)執(zhí)行任務(wù)。
  • Timer內(nèi)部是單線程執(zhí)行任務(wù)的,如果某個(gè)任務(wù)執(zhí)行時(shí)間較長,會影響后續(xù)任務(wù)的執(zhí)行。
  • 如果任務(wù)拋出未捕獲異常,將導(dǎo)致整個(gè) Timer 線程終止,影響其他任務(wù)的執(zhí)行
  • Timer 無法提供高精度的定時(shí)任務(wù)。因?yàn)橄到y(tǒng)調(diào)度和任務(wù)執(zhí)行時(shí)間的不確定性,可能導(dǎo)致任務(wù)執(zhí)行的時(shí)間不準(zhǔn)確。
  • 雖然可以使用 cancel 方法取消任務(wù),但這僅僅是將任務(wù)標(biāo)記為取消狀態(tài),仍然會在任務(wù)隊(duì)列中占用位置,無法釋放資源。這可能導(dǎo)致內(nèi)存泄漏。
  • 當(dāng)有大量任務(wù)時(shí),Timer 的性能可能受到影響,因?yàn)樗诿看螔呙枞蝿?wù)隊(duì)列時(shí)都要進(jìn)行時(shí)間比較。
  • Timer執(zhí)行任務(wù)完全基于JVM內(nèi)存,一旦應(yīng)用重啟,那么隊(duì)列中的任務(wù)就都沒有了

2. ScheduledExecutorService(JDK 1.5+,推薦)

ScheduledExecutorService基于線程池(ThreadPoolExecutor),支持多任務(wù)并發(fā)執(zhí)行。相比 Timer,更穩(wěn)定、更靈活。

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);

// 延遲 1s 后執(zhí)行
scheduler.schedule(() -> {
    System.out.println("Task executed once at: " + new Date());
}, 1, TimeUnit.SECONDS);

// 延遲 1s 后執(zhí)行,之后每 2s 執(zhí)行一次
scheduler.scheduleAtFixedRate(() -> {
    System.out.println("Fixed-rate task executed at: " + new Date());
}, 1, 2, TimeUnit.SECONDS);
優(yōu)點(diǎn)缺點(diǎn)
  • 是 JDK 1.5 之后自帶的 API,因此使用起來也比較方便
  • 執(zhí)行任務(wù)時(shí)使用線程池管理,不會造成任務(wù)間的相互影響
  • 支持 scheduleAtFixedRate(固定頻率)和 scheduleWithFixedDelay(固定間隔)。
  • 需要配置線程池
  • 如果任務(wù)拋出異常,后續(xù)任務(wù) 會被取消!需在任務(wù)內(nèi)捕獲異常。

3. Spring Task(Spring 框架支持)

適合 Spring/Spring Boot 項(xiàng)目,基于 ScheduledExecutorService,提供注解式任務(wù)調(diào)度。

@EnableScheduling
@SpringBootApplication
public class SpringbootApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }
}

@Component
public class TimerTask {
 
    @Scheduled(cron = "0 0 0 0 7 *")
    public void task() {
        System.out.println("定時(shí)任務(wù)...");
    }
}
優(yōu)點(diǎn)缺點(diǎn)
  • 注解驅(qū)動,開發(fā)簡單。
  • 支持 Cron 表達(dá)式(如 0 0 12 * * ? 表示每天中午 12 點(diǎn)執(zhí)行)。
只適合 Spring/Spring Boot 項(xiàng)目

4. Quartz(企業(yè)級任務(wù)調(diào)度框架)

Quartz是OpenSymphony開源組織在Job scheduling領(lǐng)域的一個(gè)Java實(shí)現(xiàn)的開源項(xiàng)目。

// 1. 定義 Job
public class MyJob implements Job {
    @Override
    public void execute(JobExecutionContext context) {
        System.out.println("Quartz Job executed at: " + new Date());
    }
}

// 2. 配置 Trigger 和 Scheduler
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();

JobDetail job = JobBuilder.newJob(MyJob.class)
    .withIdentity("myJob", "group1")
    .build();

Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("myTrigger", "group1")
    .startNow()
    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
        .withIntervalInSeconds(5) // 每 5s 執(zhí)行一次
        .repeatForever())
    .build();

scheduler.scheduleJob(job, trigger);
scheduler.start();
優(yōu)點(diǎn)缺點(diǎn)
  • 支持?jǐn)?shù)據(jù)庫持久化,重啟后可恢復(fù)調(diào)度
  • 支持動態(tài)調(diào)整任務(wù)(如暫停、恢復(fù)、修改執(zhí)行時(shí)間)
  • 分布式需要使用數(shù)據(jù)庫

5. DelayQueue 阻塞隊(duì)列

DelayQueue是一個(gè)帶有延遲時(shí)間的無界阻塞隊(duì)列,它的元素必須實(shí)現(xiàn)Delayed接口。當(dāng)從DelayQueue中取出一個(gè)元素時(shí),如果其延遲時(shí)間還未到達(dá),則會阻塞等待,直到延遲時(shí)間到達(dá)。因此,我們可以通過將任務(wù)封裝成實(shí)現(xiàn)Delayed接口的元素,將其放入DelayQueue中,再使用一個(gè)線程不斷地從DelayQueue中取出元素并執(zhí)行任務(wù),從而實(shí)現(xiàn)定時(shí)任務(wù)的調(diào)度。

import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

public class DelayedTask implements Delayed {
    private final String taskName;
    private final long executeTime; // 執(zhí)行時(shí)間(毫秒時(shí)間戳)

    public DelayedTask(String taskName, long delayMs) {
        this.taskName = taskName;
        this.executeTime = System.currentTimeMillis() + delayMs;
    }

    @Override
    public long getDelay(TimeUnit unit) {
        // 返回剩余延遲時(shí)間
        long remainingDelay = executeTime - System.currentTimeMillis();
        return unit.convert(remainingDelay, TimeUnit.MILLISECONDS);
    }

    @Override
    public int compareTo(Delayed other) {
        // 按執(zhí)行時(shí)間排序(早到期者優(yōu)先)
        return Long.compare(this.executeTime, ((DelayedTask) other).executeTime);
    }

    public void execute() {
        System.out.println("Executing task: " + taskName + " at " + new Date(executeTime));
    }
}

public class DelayQueueScheduler {
    private static final DelayQueue<DelayedTask> queue = new DelayQueue<>();

    public static void main(String[] args) throws InterruptedException {
        // 提交延遲任務(wù)
        queue.put(new DelayedTask("Task 1", 3000)); // 3s 后執(zhí)行
        queue.put(new DelayedTask("Task 2", 1000)); // 1s 后執(zhí)行
        queue.put(new DelayedTask("Task 3", 5000)); // 5s 后執(zhí)行

        // 啟動消費(fèi)者線程處理任務(wù)
        while (!queue.isEmpty()) {
            DelayedTask task = queue.take(); // 阻塞直到有任務(wù)到期
            task.execute();
        }
    }
}
優(yōu)點(diǎn)缺點(diǎn)
  • 精確延遲、無第三方依賴
  • 只能單進(jìn)程內(nèi)使用

6、使用 Redis 實(shí)現(xiàn)延遲任務(wù)

使用 Redis 實(shí)現(xiàn)延遲任務(wù)的方法大體可分為兩類:通過 ZSet 的方式和鍵空間通知的方式。

① ZSet 實(shí)現(xiàn)方式

通過 ZSet 實(shí)現(xiàn)定時(shí)任務(wù)的思路是,將定時(shí)任務(wù)存放到 ZSet 集合中,并且將過期時(shí)間存儲到 ZSet 的 Score 字段中,然后通過一個(gè)無線循環(huán)來判斷當(dāng)前時(shí)間內(nèi)是否有需要執(zhí)行的定時(shí)任務(wù),如果有則進(jìn)行執(zhí)行,具體實(shí)現(xiàn)代碼如下:

import redis.clients.jedis.Jedis;
import utils.JedisUtils;
import java.time.Instant;
import java.util.Set;

public class DelayQueueExample {
    // zset key
    private static final String _KEY = "myTaskQueue";
    
    public static void main(String[] args) throws InterruptedException {
        Jedis jedis = JedisUtils.getJedis();
        // 30s 后執(zhí)行
        long delayTime = Instant.now().plusSeconds(30).getEpochSecond();
        jedis.zadd(_KEY, delayTime, "order_1");
        // 繼續(xù)添加測試數(shù)據(jù)
        jedis.zadd(_KEY, Instant.now().plusSeconds(2).getEpochSecond(), "order_2");
        jedis.zadd(_KEY, Instant.now().plusSeconds(2).getEpochSecond(), "order_3");
        jedis.zadd(_KEY, Instant.now().plusSeconds(7).getEpochSecond(), "order_4");
        jedis.zadd(_KEY, Instant.now().plusSeconds(10).getEpochSecond(), "order_5");
        // 開啟定時(shí)任務(wù)隊(duì)列
        doDelayQueue(jedis);
    }

    /**
     * 定時(shí)任務(wù)隊(duì)列消費(fèi)
     * @param jedis Redis 客戶端
     */
    public static void doDelayQueue(Jedis jedis) throws InterruptedException {
        while (true) {
            // 當(dāng)前時(shí)間
            Instant nowInstant = Instant.now();
            long lastSecond = nowInstant.plusSeconds(-1).getEpochSecond(); // 上一秒時(shí)間
            long nowSecond = nowInstant.getEpochSecond();
            // 查詢當(dāng)前時(shí)間的所有任務(wù)
            Set<String> data = jedis.zrangeByScore(_KEY, lastSecond, nowSecond);
            for (String item : data) {
                // 消費(fèi)任務(wù)
                System.out.println("消費(fèi):" + item);
            }
            // 刪除已經(jīng)執(zhí)行的任務(wù)
            jedis.zremrangeByScore(_KEY, lastSecond, nowSecond);
            Thread.sleep(1000); // 每秒查詢一次
        }
    }
}

② 鍵空間通知

我們可以通過 Redis 的鍵空間通知來實(shí)現(xiàn)定時(shí)任務(wù),它的實(shí)現(xiàn)思路是給所有的定時(shí)任務(wù)設(shè)置一個(gè)過期時(shí)間,等到了過期之后,我們通過訂閱過期消息就能感知到定時(shí)任務(wù)需要被執(zhí)行了,此時(shí)我們執(zhí)行定時(shí)任務(wù)即可。

默認(rèn)情況下 Redis 是不開啟鍵空間通知的,需要我們通過 config set notify-keyspace-events Ex 的命令手動開啟,開啟之后定時(shí)任務(wù)的代碼如下:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
import utils.JedisUtils;

public class TaskExample {
    public static final String _TOPIC = "__keyevent@0__:expired"; // 訂閱頻道名稱
    public static void main(String[] args) {
        Jedis jedis = JedisUtils.getJedis();
        // 執(zhí)行定時(shí)任務(wù)
        doTask(jedis);
    }

    /**
     * 訂閱過期消息,執(zhí)行定時(shí)任務(wù)
     * @param jedis Redis 客戶端
     */
    public static void doTask(Jedis jedis) {
        // 訂閱過期消息
        jedis.psubscribe(new JedisPubSub() {
            @Override
            public void onPMessage(String pattern, String channel, String message) {
                // 接收到消息,執(zhí)行定時(shí)任務(wù)
                System.out.println("收到消息:" + message);
            }
        }, _TOPIC);
    }
}

7、使用xxl-job實(shí)現(xiàn)定時(shí)任務(wù)

xxl-job是一款分布式定時(shí)任務(wù)調(diào)度平臺,可以實(shí)現(xiàn)各種類型的定時(shí)任務(wù)調(diào)度,如定時(shí)執(zhí)行Java代碼、調(diào)用HTTP接口、執(zhí)行Shell腳本等。xxl-job采用分布式架構(gòu),支持集群部署,可以滿足高并發(fā)、大數(shù)據(jù)量的任務(wù)調(diào)度需求。

三、開源項(xiàng)目

1、https://gitee.com/xuxueli0323/xxl-job

2、https://gitee.com/dromara/disjob

3、https://gitee.com/KFCFans/PowerJob

4、https://gitee.com/aizuda/snail-job

5、https://gitee.com/elasticjob/elastic-job

到此這篇關(guān)于Java中實(shí)現(xiàn)定時(shí)任務(wù)執(zhí)行的幾種常見方式總結(jié)的文章就介紹到這了,更多相關(guān)Java定時(shí)任務(wù)執(zhí)行內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

谷城县| 盐山县| 东丽区| 天长市| 霍山县| 马尔康县| 汪清县| 若尔盖县| 凤阳县| 印江| 滨海县| 仁布县| 介休市| 新乡市| 南开区| 密云县| 乌什县| 清原| 海伦市| 玉龙| 军事| 汉源县| 大竹县| 永兴县| 房产| 文水县| 宜川县| 邹城市| 华坪县| 大英县| 桦南县| 南投县| 家居| 莒南县| 萝北县| 紫阳县| 界首市| 波密县| 河间市| 封丘县| 唐山市|