Java中如何使用定時

Java開發過程中經常會遇到使用定時任務的情況,我總結了一下有如下四種方式:Timer、ScheduledExecutorService、SpringTask、Quartz。

一、使用java的Timer

1、Timer

new Timer("testTimer").schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("TimerTask");
            }
        }, 1000,2000);

解釋:1000ms是延遲啓動時間,2000ms是定時任務週期,每2s執行一次

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
try {
	Date date = dateFormat.parse("2018-07-11 12:00:00.000");
	new Timer("testTimer1").scheduleAtFixedRate(new TimerTask() {
		@Override
		public void run() {
			System.out.println("TimerTask");
		}
	}, date,2000);
} catch (ParseException e) {
	e.printStackTrace();
}

解釋:date是開始時間,2000ms是定時任務週期,每2s執行一次
timer有2中方法schedule和scheduleAtFixedRate,前者會等任務結束在開始計算時間間隔,後者是在任務開始就計算時間,有併發的情況

二、使用ScheduledExecutorService

1、ScheduledExecutorService

scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("ScheduledTask");
            }
        },1, TimeUnit.SECONDS);

解釋:延遲1s啓動,執行一次

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("ScheduledTask");
            }
        }, 1, 1, TimeUnit.SECONDS);

解釋:延遲1s啓動,每隔1s執行一次,是前一個任務開始時就開始計算時間間隔,但是會等上一個任務結束在開始下一個

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
        scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                System.out.println("ScheduledTask");
            }
        }, 1, 1, TimeUnit.SECONDS);

解釋:延遲1s啓動,在前一個任務執行完成之後,延遲1s在執行

三、使用SpringTask

1、寫任務類

package com.zb.timedtask;
import com.zb.controller.StudentController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
public class SpringTask {
    private static final Logger log = LoggerFactory.getLogger(SpringTask.class);

    @Scheduled(cron = "1/5 * * * * *")
    public void task1(){
        log.info("springtask 定時任務!");
    }
	
	@Scheduled(initialDelay = 1000,fixedRate = 1*1000)
    public void task2(){
        log.info("springtask 定時任務!");
    }
}

解釋:
task1是每隔5s執行一次,{秒} {分} {時} {日期} {月} {星期}
task2是延遲1s,每隔1S執行一次

2、配置文件修改

(1)簡單版

<task:annotation-driven/>

(2)任務池版

<task:executor id="executor" pool-size="10" />
<task:scheduler id="scheduler" pool-size="10" />
<task:annotation-driven executor="executor" scheduler="scheduler" />

(3)解釋

假如只有一個定時任務,可以用簡單版;如果有多個定時任務,則要用任務池,不然它會順序執行。

兩個任務的時間間隔爲:執行時間+設定的定時間隔

例子:(這個任務8s執行一次)

@Scheduled(cron = "1/4 * * * * *")
public void task2(){
	log.info("springtask 定時任務2!");
	try {
		TimeUnit.SECONDS.sleep(4);
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
}

四、使用Quartz框架

1、加依賴

<!-- quartz -->
<dependency>
  <groupId>org.quartz-scheduler</groupId>
  <artifactId>quartz</artifactId>
  <version>2.3.0</version>
</dependency>
<!--調度器核心包-->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context-support</artifactId>
	<version>4.3.4.RELEASE</version>
</dependency>

2、Job實現

package com.zb.quartz;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class HelloWorldJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        String strTime = new SimpleDateFormat("HH-mm-ss").format(new Date());
        System.out.println( strTime + ":Hello World!");
    }
}

3、調度器(可以用listener在項目啓動時執行)

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class MyScheduler {
    public static void main(String[] args) throws SchedulerException {
        //創建調度器Schedule
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        //創建JobDetail實例,並與HelloWordlJob類綁定
        JobDetail jobDetail = JobBuilder.newJob(HelloWorldJob.class).withIdentity("job1", "jobGroup1")
                .build();
        //創建觸發器Trigger實例(立即執行,每隔1S執行一次)
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "triggerGroup1")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever())
                .build();
        //開始執行
        scheduler.scheduleJob(jobDetail, trigger);
        scheduler.start();
    }
}

注意:如果你的  HelloWorldJob 沒有添加

@PersistJobDataAfterExecution
@DisallowConcurrentExecution

這兩個註解的話,會默認job是資源共享的

就是這個意思:假設定時任務的時間間隔爲 3 秒,但 job 執行時間是 10 秒。當設置 @DisallowConcurrentExecution 以後程序會等任務執行完畢後再去執行,否則會在 3 秒時再啓動新的線程執行。

當設置 @PersistJobDataAfterExecution 時,在執行完 Job 的 execution 方法後保存當中固定數據和資源,以便任務在重複執行的時候具有相同的操作資源;在默認情況下也就是沒有設置 @PersistJobDataAfterExecution 的時候每個 job 都擁有獨立 操作資源,即其中的變量的值都是獨立的。

所以最好還是加上這兩個註解

解釋:上面用的是簡單觸發器,也可以用Con觸發器,如下

Trigger cronTrigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger2", "triggerGroup2")
                .startNow()
                .withSchedule(cronSchedule("0 42 10 * * ?"))
                .build();

4、整合spring

也可以直接把上面的調度器寫成配置文件,整合spring

(1)job

package com.zb.quartz;
import java.text.SimpleDateFormat;
import java.util.Date;

public class QuarFirstJob {
    public void first() {
        String strTime = new SimpleDateFormat("HH-mm-ss").format(new Date());
        System.out.println( strTime + ":Hello World!");
    }
}

(2)配置文件

<bean id="QuarFirstJob" class="com.zb.quartz.QuarFirstJob" />

<bean id="jobDetail"
	  class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
	<property name="group" value="quartzGroup1" />
	<property name="name" value="quartzJob1" />
	<!--false表示等上一個任務執行完後再開啓新的任務 -->
	<property name="concurrent" value="false" />
	<property name="targetObject">
		<ref bean="QuarFirstJob" />
	</property>
	<property name="targetMethod">
		<value>first</value>
	</property>
</bean>

<!-- 調度觸發器 -->
<bean id="myTrigger"
	  class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
	<property name="name" value="trigger1" />
	<property name="group" value="group1" />
	<property name="jobDetail">
		<ref bean="jobDetail" />
	</property>
	<property name="cronExpression">
		<value>0/5 * * * * ?</value>
	</property>
</bean>

<!-- 調度工廠 -->
<bean id="scheduler"
	  class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
	<property name="triggers">
		<list>
			<ref bean="myTrigger" />
		</list>
	</property>
</bean>

5、時間

public class QuarFirstJob {
    public void first() {
        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String strTime = new SimpleDateFormat("HH-mm-ss").format(new Date());
        System.out.println( strTime + ":Hello World!");
    }
}

上面的配置裏面寫是5s間隔,把上面的sleep時間分別改成4和6,發現兩次任務間隔是執行時間和間隔時間的最大值,分別是5,6

總結

Quartz是執行時間和間隔時間的最大值(比如;執行時間是3s,間隔是2s,則每3s執行一次;執行時間是3s,間隔是5s,則每5s執行一次。)
Spring task是執行時間+間隔時間(比如;執行時間是3s,間隔是2s,則每5s執行一次;執行時間是3s,間隔是5s,則每8s執行一次。)
timer有2中方法schedule和scheduleAtFixedRate,前者會等任務結束在開始計算時間間隔,後者是在任務開始就計算時間,有併發的情況
ScheduledExecutorService的scheduleAtFixedRate類似Quartz,scheduleWithFixedDelay類似SpringTask

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章