什么是定时任务
定时任务是指基于给定的时间点、给定的时间间隔或者给定的执行次数自动执行一项或多项任务。
常用的定时任务如下。
1.crontab命令
直接使用linux系统自带的crontab程序进行定时任务控制。
2.JDK Timer
java.util.Timer定时器为单线程,定时调度所拥有的TimeTask任务。
public class HelloTimerTask extends TimerTask{
@Override
public void run() {
System.out.println(new Date());
}
}
public class HelloTimer {
public static void main(String[] args) {
Timer timer = new Timer();
System.out.println(new Date());
timer.schedule(new HelloTimerTask(), 1000, 2000);
}
}
Timer类缺陷如下:
- 时间延迟不准确。由于是单线程执行,如果某个任务执行时间过长,可能导致后续任务时间延迟。
- 异常终止。如果任务抛出未捕获的异常,会导致Timer线程终止,所有任务终止。
- 执行周期任务时依赖系统时间。当系统时间发生变化,会导致执行上的变化。
3.ScheduledExecutor
public class HelloTask implements Runnable{
@Override
public void run() {
System.out.println(new Date());
}
}
public class HelloScheduledExecutor {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
executor.schedule(new HelloTask(),1,TimeUnit.SECONDS);
executor.scheduleAtFixedRate(new HelloTask(), 1, 2, TimeUnit.SECONDS);
executor.scheduleWithFixedDelay(new HelloTask(), 1, 2, TimeUnit.SECONDS);
}
}
ScheduledThreadPool内部使用线程池实现,各个任务之间并发执行,互相隔离
4.Spring Scheduler
public class HelloTask {
public void doTask() {
SimpleDateFormat sdf = new SimpleDateFormat("YYYY/MM/dd HH:mm:ss.SSS");
System.out.println("hello world " + sdf.format(new Date()));
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.1.xsd">
<bean id="helloTask" class="com.test.SpringScheduler.HelloTask"></bean>
<task:scheduler id="myScheduler" pool-size="3" />
<task:scheduled-tasks scheduler="myScheduler">
<task:scheduled ref="helloTask" method="doTask" cron="*/3 * * * * ?" />
</task:scheduled-tasks>
</beans>
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
5.Quartz
传统定时任务存在的问题
业务发展初期,批量任务可以部署在单一服务器上,上文的定时任务实现方式均可以实现,但是随着业务增多,全部作业调度任务放在一台机器上执行存在明显缺陷:单点风险和资源分布不均。
如何将任务分散到不同机器上执行
1.通过配置参数分散运行,例如在不同机器的properties文件中配置不同的任务;
2.通过分布式锁互斥执行,详见https://blog.youkuaiyun.com/penguinhao/article/details/83515024
分布式定时任务的两种处理方式
- 抢占式:谁先获得资源谁执行,这种模式无法将单个任务的数据交给其他节点协同处理,一般用于处理数据量较小、任务较多的场景。
- 协同式:可以将单个任务处理的数据均分到多个JVM中处理,提高数据的并行处理能力。
开源框架
Quartz的分布式模式
TBSchedule
Elastic-Job