java定時任務的實現方式


本文列舉常見的java定時任務實現方式,並做一定比較。

1. 循環內部sleep實現周期執行

創建一個thread,run() while循環里sleep()來實現周期性執行; 簡單粗暴,作為一個初學者很容易想到。

public class Task1 {
	public static void main(String[] args) {
		// run in a second
		final long timeInterval = 1000;
		Runnable runnable = new Runnable() {
			public void run() {
				while (true) {
					System.out.println("Hello !!");
					// 使用線程休眠來實現周期執行,
					try {
						Thread.sleep(timeInterval);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		};
		Thread thread = new Thread(runnable);
		thread.start();
	}
}

2. 使用Timer類調度TimerTask任務

改進:當啟動和去取消任務時可以控制; 第一次執行任務時可以指定你想要的delay時間
不足:

  • Timer的調度是基於絕對時間的,所以當系統時間改變時會影響Timer。
  • Timer只有一個工作線程,所以當一個任務執行時間很長的時候,會影響后續任務的調度。
    而ScheduledThreadPoolExecutor通過線程池的方式配置更靈活。
  • 如果任務拋出了一個未檢查的異常,將會導致Timer的工作線程被終止,使Timer無法在繼續運行。
import java.util.Timer;
import java.util.TimerTask;

public class HelperTest {
	public static void main(String[] args) {
		// 具體任務。
		TimerTask task = new TimerTask() {
			@Override
			public void run() {
				// task to run goes here
				System.out.println("Hello !!!");
			}
		};

		// Timer類可以調度任務。 Timer實例可以調度多任務,它是線程安全的。
		Timer timer = new Timer();
		long delay = 0;
		long intevalPeriod = 1 * 1000;
		// schedules the task to be run in an interval
		timer.scheduleAtFixedRate(task, delay, intevalPeriod);
	}
}

3. 使用j.u.c.ScheduledExecutorService定時任務接口

  1. 相比於Timer的單線程,它是通過線程池的方式來執行任務的
  2. 可以靈活的設定第一次執行任務delay時間
  3. 提供了良好的約定,以便設定執行的時間間隔
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Task3 {
	public static void main(String[] args) {


		ScheduledExecutorService service = new ScheduledThreadPoolExecutor(1);

		// 初始化延遲0ms開始執行,每隔200ms重新執行一次任務。
                ScheduledExecutorService  pool = new ScheduledThreadPoolExecutor(1);
                pool.scheduleAtFixedRate(new Runnable() {
                    @Override
                    public void run() {
    				// task to run goes here
				System.out.println("Hello !");
                    }
                }, 0, 200L, TimeUnit.MILLISECONDS);
}

實現類使用的是ScheduledThreadPoolExecutor。該類繼承自ThreadPoolExecutor read more,阻塞隊列使用的是DelayedWorkQueue,是ScheduledThreadPoolExecutor的內部類。

ScheduledThreadPoolExecutor類圖

ScheduledExecutorService接口方法說明:
ScheduledExecutorService接口方法

其中scheduleAtFixedRate和scheduleWithFixedDelay在實現定時程序時比較方便。

  • scheduleAtFixedRate(runnable, 0, 200L, TimeUnit.MILLISECONDS) 按指定周期執行某個任務
    初始化延遲0ms開始執行,每隔200ms重新執行一次任務。

  • scheduleWithFixedDelay(runnable, 0, 200L, TimeUnit.MILLISECONDS) 按指定間隔執行某個任務
    初始化時延時0ms開始執行,下次執行時間是(本次執行結束 + 延遲200ms)后開始執行。

  • schedule(Runnable command, long delay, TimeUnit unit) 在delay延時后執行一次性任務

備注:對於scheduleAtFixedRate,實際上如果當前線程阻塞執行時間t > 設置的間隔時間period,下次是在t時間后執行,並非period時間后立即開始。


ScheduledExecutorService的spring配置

>> spring.xml

    <bean id="gkHeartBeatScheduler" class="org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean">
        <property name="poolSize" value="4"/>
        <property name="threadNamePrefix" value="gkHeartBeat"/>
    </bean>

>> xxx.java
    @Autowired
    @Qualifier("gkHeartBeatScheduler")
    ScheduledExecutorService scheduledExecutorService;

    scheduledExecutorService.scheduleAtFixedRate(
                new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("do sth");
                    }
                }, 1l, 2l, TimeUnit.SECONDS);

spring ScheduledExecutorFactoryBean內部同樣使用的ScheduledThreadPoolExecutor,並對其做了包裝處理。

public class ScheduledExecutorFactoryBean extends ExecutorConfigurationSupport implements FactoryBean<ScheduledExecutorService>

4. @Sheduled注解方式

@Sheduled內部也使用了ScheduledThreadPoolExecutor。具體源代碼可參見:spring-context包中的ScheduledAnnotationBeanPostProcessor。

用法就很簡單了,舉例:

  1. pom文件引入spring-context依賴
  2. 使用注解方式配置定時任務即可
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
 
@Component
@EnableScheduling
public class ScheduledAnnotationDemo {

	// @Scheduled和觸發器元素一起添加到方法上.

	@Scheduled(fixedDelay=5000)
	public void doSomething() {
		System.out.println("like scheduleWithFixedDelay");          
	}
	@Scheduled(fixedRate=5000)
	public void doSomething() {
		System.out.println("like scheduleAtFixedRate");        
	}
	// fixed-delay、fixed-rate任務都可以設置初始delay。
	@Scheduled(initialDelay=1000, fixedRate=5000)
	public void doSomething() {
		// something that should execute periodically
	}

	// 也支持cron表達式
	@Scheduled(cron = "0/5 * * * * ?")
	public void doSomething() {
		// something that should execute on weekdays only
		System.out.println("5s執行一次");    
	}
	//cron舉例:(秒 - 分 - 時 - 日 - 月- 星期)
	//    */5 * * * * ?     每隔5秒執行一次      
	//    0 */1 * * * ?     每隔1分鍾執行一次  
	//    0 0 1 * * ?       每天1點執行一次 
	//    0 0 1 1 * ?       每月1號1點執行一次
	//    0 0 1 L * ?       每月最后一天1點執行一次
	//    0 0 1 ? * L       每周星期天1點執行一次
}

上面使用@EnableScheduling的方式啟動定時任務,等價於在spring xml中配置<task:annotation-driven />元素。

5. 開源任務調度框架Quartz

Quartz , 功能強大的任務調度庫。適用於具有更復雜調度要求的場景。
提供了對持久化任務調度信息、事務、分布式的支持。與spring無縫對接。

參見:quartz調度基礎: Job/Trigger/Schedule.

6. 小結

  • 使用ScheduledThreadPoolExecutor完成簡單定時任務,是比較理想和常用的實現方式。書寫時更容易理解其過程實現。
  • 也可以用@Sheduled注解的形式,更加輕量化,看起來更簡潔。
  • 對復雜的任務調度,可以使用Quartz框架。

參考:
@Scheduled-vs-Quartz
Task Execution and Scheduling


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM