Java實現定時任務最簡單的3種方法

一、Timer

Timer是JAVA自帶的定時任務類,實現如下:

public class MyTimerTask {    
    public static void main(String[] args) {        
        // 定義一個任務       
        TimerTask timerTask = new TimerTask() {            
        @Override            
            public void run() {                
            System.out.println("打印當前時間:" + new Date());    
            }       
         };        
        // 計時器       
        Timer timer = new Timer();       
        // 開始執行任務 (延遲1000毫秒執行,每3000毫秒執行一次)        
        timer.schedule(timerTask, 1000, 3000);    
    }
}

Timer 優缺點分析

優點是使用簡單,缺點是當添加並執行多個任務時,前面任務的執行用時和異常將影響到後面任務,這邊深海建議謹慎使用。

二、ScheduledExecutorService

ScheduledExecutorService 也是Java自帶的類,

它可以實現Timer具備的所有功能,並解決瞭 Timer類存在的問題

實現如下:

public class MyScheduledExecutorService {    
    public static void main(String[] args) {        
        // 創建任務隊列   10 為線程數量      
        ScheduledExecutorService scheduledExecutorService = 
                Executors.newScheduledThreadPool(10); 
        // 執行任務      
        scheduledExecutorService.scheduleAtFixedRate(() -> {          
            System.out.println("打印當前時間:" + new Date());      
        }, 1, 3, TimeUnit.SECONDS); // 1s 後開始執行,每 3s 執行一次   
 
  }
}

ScheduledExecutorService 優缺點分析

優點是,該類是JDK1.5自帶的類,使用簡單,缺點是該方案僅適用於單機環境。

三、Spring Task

Spring系列框架中Spring Framework自帶的定時任務,

使用上面兩種方式,很難實現某些特定需求,比如每周一執行某任務,但SpringTask可輕松實現。

以SpringBoot為例來實現:

1、開啟定時任務

在SpringBoot的啟動類上聲明 @EnableScheduling:

@SpringBootApplication
@EnableScheduling //開啟定時任務
public class DemoApplication {  
     // --  -- 
}

2、添加定時任務

隻需使用@Scheduled註解標註即可,

如果有多個定時任務,可以創建多個@Scheduled標註的方法,示例如下:

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component // 把此類托管給 Spring,不能省略
public class TaskUtils {    
    // 添加定時任務    
    @Scheduled(cron = "30 40 23 0 0 5") // cron表達式:每周一 23:40:30 執行    
    public void doTask(){        
        System.out.println("我是定時任務~");    
    }
}

 Spring Boot 啟動後會自動加載並執行定時任務,無需手動操作。

Cron 表達式

Spring Task 的實現需要使用 cron 表達式來聲明執行的頻率和規則,cron 表達式是由 6 位或者 7 位組成的(最後一位可以省略),每位之間以空格分隔,每位從左到右代表的含義如下:

其中 * 和 ? 號都表示匹配所有的時間。

cron 表達式在線生成地址:https://cron.qqe2.com/

知識擴展:分佈式定時任務

上面的方法都是關於單機定時任務的實現,如果是分佈式環境可以使用 Redis 來實現定時任務。

使用 Redis 實現延遲任務的方法大體可分為兩類:通過 ZSet 的方式和鍵空間通知的方式

1、ZSet 實現方式

通過 ZSet 實現定時任務的思路是,將定時任務存放到 ZSet 集合中,並且將過期時間存儲到 ZSet 的 Score 字段中,然後通過一個無線循環來判斷當前時間內是否有需要執行的定時任務,如果有則進行執行,具體實現代碼如下:

import redis.clients.jedis.Jedis;
import utils.JedisUtils;
import java.time.Instant;
import java.util.Set;
public class DelayQueueExample {        
    private static final String _KEY = "DelayQueueExample";        
    public static void main(String[] args) throws InterruptedException {        
        Jedis jedis = JedisUtils.getJedis();        
        // 30s 後執行        
        long delayTime = Instant.now().plusSeconds(30).getEpochSecond();       
        jedis.zadd(_KEY, delayTime, "order_1");        
        // 繼續添加測試數據        
       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");        
        // 開啟定時任務隊列        
        doDelayQueue(jedis);    
    }    
    /**     
    * 定時任務隊列消費     
    * @param jedis Redis 客戶端     
    */    
    public static void doDelayQueue(Jedis jedis) throws InterruptedException {        
        while (true) {            
            // 當前時間            
            Instant nowInstant = Instant.now();            
            long lastSecond = nowInstant.plusSeconds(-1).getEpochSecond(); 
            // 上一秒時間            
            long nowSecond = nowInstant.getEpochSecond();            
            // 查詢當前時間的所有任務            
            Set data = jedis.zrangeByScore(_KEY, lastSecond, nowSecond);            
            for (String item : data) {                
            // 消費任務                
            System.out.println("消費:" + item);            
        }            
        // 刪除已經執行的任務            
        jedis.zremrangeByScore(_KEY, lastSecond, nowSecond);            
        Thread.sleep(1000); // 每秒查詢一次        
        }    
    }
}

2、鍵空間通知

我們可以通過 Redis 的鍵空間通知來實現定時任務,它的實現思路是給所有的定時任務設置一個過期時間,等到瞭過期之後,我們通過訂閱過期消息就能感知到定時任務需要被執行瞭,此時我們執行定時任務即可。

默認情況下 Redis 是不開啟鍵空間通知的,需要我們通過 config set notify-keyspace-events Ex 的命令手動開啟,開啟之後定時任務的代碼如下:

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

總結

到此這篇關於Java實現定時任務最簡單的3種方法的文章就介紹到這瞭,更多相關Java定時任務實現內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: