关于java:学会这10种定时任务我有点飘了

5次阅读

共计 12933 个字符,预计需要花费 33 分钟才能阅读完成。

前言

最近有几个读者私信给我,问我他们的业务场景,要用什么样的定时工作。的确,在不必的业务场景下要用不同的定时工作,其实咱们的抉择还是挺多的。我明天给大家总结 10 种十分实用的定时工作,总有一种是适宜你的。

一. linux 自带的定时工作

crontab

不晓得你有没有遇到过这种场景:有时须要长期统计线上的数据,而后导出到 excel 表格中。这种需要有时较为简单,光靠写 sql 语句是无奈满足需要的,这就须要写 java 代码了。而后将该程序打成一个 jar 包,在线上环境执行,最初将生成的 excel 文件下载到本地。

为了减小对线上环境的影响,咱们个别会抉择在凌晨 1 - 2 点,趁用户量少的时候,执行统计程序。(其实凌晨 4 点左右,用户才是起码的)

因为工夫太晚了,咱们齐全没必要守在那里等执行后果,一个定时工作就能能够搞定。

那么,这种状况用哪种定时工作更适合呢?

答案是:linux 零碎的 crontab。(不过也不排除有些我的项目没部署在 linux 零碎中)

运行 crontab -e,能够编辑定时器,而后退出如下命令:

0 2 * * * /usr/local/java/jdk1.8/bin/java -jar /data/app/tool.jar > /logs/tool.log &

就能够在每天凌晨 2 点,定时执行 tool.jar 程序,并且把日志输入到 tool.log 文件中。当然你也能够把前面的执行 java 程序的命令写成 shell 脚本,更不便保护。

应用这种定时工作反对不便批改定时规定,有界面能够对立治理配置的各种定时脚本。

crontab 命令的根本格局如下:

crontab [参数] [文件名]

如果没有指定文件名,则接管键盘上输出的命令,并将它载入到 crontab。

参数性能对照表如下:

以上参数,如果没有应用 - u 指定用户,则默认应用的以后用户。

通过 crontab - e 命令编辑文件内容,具体语法如下:

[分] [小时] [日期] [月] [星期] 具体任务

其中:

  • 分,示意多少分钟,范畴:0-59
  • 小时,示意多少小时,范畴:0-23
  • 日期,示意具体在哪一天,范畴:1-31
  • 月,示意多少月,范畴:1-12
  • 星期,示意多少周,范畴:0-7,0 和 7 都代表星期日

还有一些特殊字符,比方:
代表如何工夫,比方:1* 示意每天凌晨 1 点执行。
• / 代表每隔多久执行一次,比方:/5 * 示意每隔 5 分钟执行一次。
• , 代表反对多个,比方:10 7,9,12 * 示意在每天的 7、9、12 点 10 分各执行一次。
• - 代表反对一个范畴,比方:10 7-9 * 示意在每天的 7、8、9 点 10 分各执行一次。

此外,顺便说一下 crontab 须要 crond 服务反对,crond 是 linux 下用来周期地执行某种工作的一个守护过程,在装置 linux 操作系统后,默认会装置 crond 服务工具,且 crond 服务默认就是自启动的。crond 过程每分钟会定期检查是否有要执行的工作,如果有,则会主动执行该工作。

能够通过以下命令操作相干服务:

service crond status // 查看运行状态
service crond start // 启动服务
service crond stop // 敞开服务
service crond restart // 重启服务
service crond reload // 从新载入配置

应用 crontab 的优缺点:

• 长处:不便批改定时规定,反对一些较简单的定时规定,通过文件能够对立治理配好的各种定时脚本。
• 毛病:如果定时工作十分多,不太好找,而且必须要求操作系统是 linux,否则无奈执行。

二. jdk 自带的定时工作

1.Thread

各位敬爱的敌人,你没看错,Thread类真的能做定时工作。如果你看过一些定时工作框架的源码,你最初会发现,它们的底层也会应用 Thread 类。实现这种定时工作的具体代码如下:

public static void init() {new Thread(() -> {while (true) {
            try {System.out.println("doSameThing");
                Thread.sleep(1000 * 60 * 5);
            } catch (Exception e) {log.error(e);
            }
        }
    }).start();}

应用 Thread 类能够做最简略的定时工作,在 run 办法中有个 while 的死循环(当然还有其余形式),执行咱们本人的工作。有个须要特地留神的中央是,须要用 try...catch 捕捉异样,否则如果出现异常,就间接退出循环,下次将无奈继续执行了。这种形式做的定时工作,只能周期性执行,不能反对定时在某个工夫点执行。此外,该线程能够定义成 守护线程 ,在后盾默默执行就好。应用场景:比方我的项目中有时须要每隔 10 分钟去下载某个文件,或者每隔 5 分钟去读取模板文件生成动态 html 页面等等,一些简略的周期性工作场景。 应用 Thread 类的优缺点:

  • 长处:这种定时工作非常简单,学习成本低,容易动手,对于那些简略的周期性工作,是个不错的抉择。
  • 毛病:不反对指定某个工夫点执行工作,不反对提早执行等操作,性能过于繁多,无奈应答一些较为简单的场景。

2.Timer

Timer类是 jdk 专门提供的定时器工具,用来在后盾线程打算执行指定工作,在 java.util 包下,要跟 TimerTask 一起配合应用。

Timer类其实是一个任务调度器,它外面蕴含了一个 TimerThread 线程,在这个线程中有限循环从 TaskQueue 中获取 TimerTask(该类实现了 Runnable 接口),调用其run 办法,就能异步执行定时工作。咱们须要继承 TimerTask 类,实现它的 run 办法,在该办法中加上本人的业务逻辑。实现这种定时工作的具体代码如下:

public class TimerTest {public static void main(String[] args) {Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {System.out.println("doSomething");
            }
        },2000,1000);
    }
}

先实例化一个 Timer 类,而后调用它的 schedule 办法,在该办法中实例化 TimerTask 类,业务逻辑写在 run 办法中。schedule办法最初的两次参数别离示意:延迟时间 间隔时间,单位是毫秒。下面例子中,设置的定时工作是每隔 1 秒执行一次,提早 2 秒执行。次要蕴含 6 个办法:

  • schedule(TimerTask task, Date time), 指定工作 task 在指定工夫 time 执行
  • schedule(TimerTask task, long delay), 指定工作 task 在指定提早 delay 后执行
  • schedule(TimerTask task, Date firstTime,long period), 指定工作 task 在指定工夫 firstTime 执行后,进行反复固定提早频率 peroid 的执行
  • schedule(TimerTask task, long delay, long period), 指定工作 task 在指定提早 delay 后,进行反复固定提早频率 peroid 的执行
  • scheduleAtFixedRate(TimerTask task,Date firstTime,long period), 指定工作 task 在指定工夫 firstTime 执行后,进行反复固定提早频率 peroid 的执行
  • scheduleAtFixedRate(TimerTask task, long delay, long period), 指定工作 task 在指定提早 delay 后,进行反复固定提早频率 peroid 的执行

不过应用 Timer 实现定时工作有以下问题:

  1. 因为 Timer 是单线程执行工作,如果其中一个工作耗时十分长,会影响其余工作的执行。
  2. 如果 TimerTask 抛出RuntimeException,Timer 会进行所有工作的运行。

应用 Timer 类的优缺点:

  • 长处:十分不便实现多个周期性的定时工作,并且反对提早执行,还反对在指定工夫之后反对,性能还算弱小。
  • 毛病:如果其中一个工作耗时十分长,会影响其余工作的执行。并且如果 TimerTask 抛出 RuntimeExceptionTimer 会进行所有工作的运行,所以阿里巴巴开发者标准中不倡议应用它。

3.ScheduledExecutorService

ScheduledExecutorService是 JDK1.5+ 版本引进的定时工作,该类位于 java.util.concurrent 并发包下。ScheduledExecutorService是基于多线程的,设计的初衷是为了解决 Timer 单线程执行,多个工作之间会相互影响的问题。它次要蕴含 4 个办法:

  • schedule(Runnable command,long delay,TimeUnit unit),带延迟时间的调度,只执行一次,调度之后可通过 Future.get()阻塞直至工作执行结束。
  • schedule(Callable<V> callable,long delay,TimeUnit unit),带延迟时间的调度,只执行一次,调度之后可通过 Future.get()阻塞直至工作执行结束,并且能够获取执行后果。
  • scheduleAtFixedRate,示意以固定频率执行的工作,如果当前任务耗时较多,超过定时周期 period,则当前任务完结后会立刻执行。
  • scheduleWithFixedDelay,示意以固定延时执行工作,延时是绝对当前任务完结为终点计算开始工夫。

实现这种定时工作的具体代码如下:

public class ScheduleExecutorTest {public static void main(String[] args) {ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        scheduledExecutorService.scheduleAtFixedRate(() -> {System.out.println("doSomething");
        },1000,1000, TimeUnit.MILLISECONDS);
    }
}

调用 ScheduledExecutorService 类的 scheduleAtFixedRate 办法实现周期性工作,每隔 1 秒钟执行一次,每次提早 1 秒再执行。这种定时工作是阿里巴巴开发者标准中用来代替 Timer 类的计划,对于多线程执行周期性工作,是个不错的抉择。ScheduledExecutorService 的优缺点:

  • 长处:基于多线程的定时工作,多个工作之间不会相干影响,反对周期性的执行工作,并且带提早性能。
  • 毛病:不反对一些较简单的定时规定。

三. spring 反对的定时工作

1.spring task

spring taskspring3 以上版本自带的定时工作,实现定时工作的性能时,须要引入 spring-context 包,目前它反对:xml注解 两种形式。

1. 我的项目实战

因为 xml 形式太古老了,咱们以 springboot 我的项目中注解形式为例。第一步 ,在 pom.xml 文件中引入spring-context 相干依赖。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
</dependency>

第二步 ,在 springboot 启动类上加上@EnableScheduling 注解。

@EnableScheduling
@SpringBootApplication
public class Application {public static void main(String[] args) {new SpringApplicationBuilder(Application.class).web(WebApplicationType.SERVLET).run(args);
    }
}

第三步 ,应用@Scheduled 注解定义定时规定。

@Service
public class SpringTaskTest {@Scheduled(cron = "${sue.spring.task.cron}")
    public void fun() {System.out.println("doSomething");
    }
}

第四步 ,在applicationContext.properties 文件中配置参数:

sue.spring.task.cron=*/10 * * * * ?

这样就能每隔 10 秒执行一次 fun 办法了。

2. cron 规定

spring4 以上的版本中,cron 表达式蕴含 6 个参数:

[秒] [分] [时] [日期] [月] [星期]

还反对几个罕用的特殊符号:

  • *:示意任何工夫触发工作
  • ,:示意指定的工夫触发工作
  • -:示意一段时间内触发工作
  • /:示意从哪一个时刻开始,每隔多长时间触发一次工作。
  • ?:示意用于月中的天和周中的天两个子表达式,示意不指定值。

cron 表达式参数具体含意:

  1. 秒,取值范畴:0-59,反对*,-/
  2. 分,取值范畴:0-59,反对*,-/
  3. 时,取值范畴:0-23,反对*,-/
  4. 日期,取值范畴:1-31,反对 *,-/。比秒多了?,示意如果指定的 星期 触发了,则配置的 日期 变成有效。
  5. 月,取值范畴:1-12,反对*,-/
  6. 星期,取值范畴:1~7,1 代表星期天,6 代表星期六,其余的以此类推。反对 *,-/?。比秒多了?,示意如果指定的 日期 触发了,则配置的 星期 变成有效。

常见 cron 表达式应用举例:

  • 0 0 0 1 * ? 每月 1 号零点执行
  • 0 0 2 * * ? 每天凌晨 2 点执行
  • 0 0 2 * * ? 每天凌晨 2 点执行
  • 0 0/5 11 * * ? 每天 11 点 -11 点 55 分,每隔 5 分钟执行一次
  • 0 0 18 ? * WED 每周三下午 6 点执行

spring task 先通过 ScheduledAnnotationBeanPostProcessor 类的 processScheduled 办法,解析和收集 Scheduled 注解中的参数,蕴含:cron 表达式。而后在 ScheduledTaskRegistrar 类的 afterPropertiesSet 办法中,默认初始化一个单线程的 ThreadPoolExecutor 执行工作。对 spring task 感兴趣的小伙伴,能够加我微信找我私聊。应用 spring task 的优缺点:

  • 长处:spring 框架自带的定时性能,springboot 做了十分好的封装,开启和定义定时工作非常容易,反对简单的 cron 表达式,能够满足绝大多数单机版的业务场景。单个工作时,以后次的调度实现后,再执行下一次任务调度。
  • 毛病:默认单线程,如果后面的工作执行工夫太长,对前面工作的执行有影响。不反对集群形式部署,不能做数据存储型定时工作。

2.spring quartz

quartzOpenSymphony 开源组织在 Job scheduling 畛域的开源我的项目,是由 java 开发的一个开源的工作日程管理系统。quartz 能做什么?

  • 作业调度:调用各种框架的作业脚本,例如 shell,hive 等。
  • 定时工作:在某一预约的时刻,执行你想要执行的工作。

架构图如下:

quartz 蕴含的次要接口如下:

  • Scheduler 代表调度容器,一个调度容器中能够注册多个 JobDetail 和 Trigger。
  • Job 代表工作,即要执行的具体内容。
  • JobDetail 代表具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容。
  • JobBuilder 用于定义或构建 JobDetail 实例。
  • Trigger 代表调度触发器,决定什么时候去调。
  • TriggerBuilder 用于定义或构建触发器。
  • JobStore 用于存储作业和任务调度期间的状态。

1. 我的项目实战

咱们还是以 springboot 集成 quartz 为例。第一步 ,在 pom.xml 文件中引入quartz 相干依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

第二步,创立真正的定时工作执行类,该类继承QuartzJobBean

public class QuartzTestJob extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {String userName = (String) context.getJobDetail().getJobDataMap().get("userName");
        System.out.println("userName:" + userName);
    }
}

第三步 ,创立调度程序JobDetail 和调度器Trigger

@Configuration
public class QuartzConfig {@Value("${sue.spring.quartz.cron}")
    private String testCron;

    /**
     * 创立定时工作
     */
    @Bean
    public JobDetail quartzTestDetail() {JobDetail jobDetail = JobBuilder.newJob(QuartzTestJob.class)
                .withIdentity("quartzTestDetail", "QUARTZ_TEST")
                .usingJobData("userName", "susan")
                .storeDurably()
                .build();
        return jobDetail;
    }

    /**
     * 创立触发器
     */
    @Bean
    public Trigger quartzTestJobTrigger() {
        // 每隔 5 秒执行一次
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(testCron);

        // 创立触发器
        Trigger trigger = TriggerBuilder.newTrigger()
                .forJob(quartzTestDetail())
                .withIdentity("quartzTestJobTrigger", "QUARTZ_TEST_JOB_TRIGGER")
                .withSchedule(cronScheduleBuilder)
                .build();
        return trigger;
    }
}

第四步 ,在applicationContext.properties 文件中配置参数:

sue.spring.quartz.cron=*/5 * * * * ?

这样就能每隔 5 秒执行一次 QuartzTestJob 类的 executeInternal 办法了。CronTrigger 配置格局:

[秒] [分] [小时] [日] [月] [周] [年]

spring quartzspring taskcron表达式规定基本一致,只是 spring4 以上的版本去掉了前面的 ,而 quartzCronTrigger是非必填的,这里我就不做过多介绍了。应用 spring quartz 的优缺点:

  • 长处:默认是多线程异步执行,单个工作时,在上一个调度未实现时,下一个调度工夫到时,会另起一个线程开始新的调度,多个工作之间互不影响。反对简单的 cron 表达式,它能被集群实例化,反对分布式部署。
  • 毛病:绝对于 spring task 实现定时工作老本更高,须要手动配置 QuartzJobBeanJobDetailTrigger等。须要引入了第三方的 quartz 包,有肯定的学习老本。不反对并行调度,不反对失败解决策略和动静分片的策略等。

四. 分布式定时工作

1.xxl-job

xxl-job是公众点评(许雪里)开发的一个分布式任务调度平台,其外围设计指标是开发迅速、学习简略、轻量级、易扩大。现已凋谢源代码并接入多家公司线上产品线,开箱即用。xxl-job框架对 quartz 进行了扩大,应用 mysql 数据库存储数据,并且内置 jetty 作为 RPC 服务调用。次要特点如下:

  1. 有界面保护定时工作和触发规定,非常容易治理。
  2. 能动静启动或进行工作
  3. 反对弹性扩容缩容
  4. 反对工作失败报警
  5. 反对动静分片
  6. 反对故障转移
  7. Rolling 实时日志
  8. 反对用户和权限治理

治理界面:

整体架构图如下:

应用 quartz 架构图如下:

我的项目实战

xxl-admin治理后盾部署和 mysql 脚本执行等这些后期筹备工作,我就不过多介绍了,有需要的敌人能够找我私聊,这些更偏差于运维的事件。假如前期工作曾经 OK 了,接下来咱们须要:第一步 ,在 pom.xml 文件中引入xxl-job 相干依赖。

<dependency>
   <groupId>com.xuxueli</groupId>
   <artifactId>xxl-job-core</artifactId>
</dependency>

第二步 ,在applicationContext.properties 文件中配置参数:

xxl.job.admin.address: http://localhost:8088/xxl-job-admin/
xxl.job.executor.appname: xxl-job-executor-sample
xxl.job.executor.port: 8888
xxl.job.executor.logpath: /data/applogs/xxl-job/

第三步 ,创立 HelloJobHandler 类继承IJobHandler 类:

@JobHandler(value = "helloJobHandler")
@Component
public class HelloJobHandler extends IJobHandler {

    @Override
    public ReturnT<String> execute(String param) {System.out.println("XXL-JOB, Hello World.");
        return SUCCESS;
    }
}

这样定时工作就配置好了。

倡议把定时工作独自部署到另外一个服务中,跟 api 服务离开。依据我以往的教训,job 大部分状况下,会对数据做批量操作,如果操作的数据量太大,可能会对服务的内存和 cpu 资源造成肯定的影响。

应用 xxl-job 的优缺点:

  • 长处:有界面治理定时工作,反对弹性扩容缩容、动静分片、故障转移、失败报警等性能。它的性能十分弱小,很多大厂在用,能够满足绝大多数业务场景。
  • 毛病:和 quartz 一样,通过数据库分布式锁,来管制工作不能反复执行。在工作十分多的状况下,有一些性能问题。

2.elastic-job

elastic-job是当当网开发的弹性分布式任务调度零碎,功能丰富弱小,采纳 zookeeper 实现分布式协调,实现工作高可用以及分片。它是专门为高并发和简单业务场景开发。elastic-job目前是 apacheshardingsphere我的项目下的一个子项目,官网地址:http://shardingsphere.apache….。elastic-job在 2.x 之后,出了两个产品线:Elastic-Job-LiteElastic-Job-Cloud,而咱们个别应用 Elastic-Job-Lite 就可能满足需要。Elastic-Job-Lite 定位为轻量级无中心化解决方案,应用 jar 包的模式提供分布式工作的协调服务,内部仅依赖于 Zookeeper。。次要特点如下:

  • 散布式调度协调
  • 弹性扩容缩容
  • 生效转移
  • 错过执行作业重触发
  • 作业分片一致性,保障同一分片在分布式环境中仅一个执行实例
  • 自诊断并修复分布式不稳固造成的问题
  • 反对并行调度

整体架构图:

我的项目实战

第一步 ,在 pom.xml 文件中引入elastic-job 相干依赖。

<dependency>
    <groupId>com.dangdang</groupId>
    <artifactId>elastic-job-lite-core</artifactId>
</dependency>
<dependency>
    <groupId>com.dangdang</groupId>
    <artifactId>elastic-job-lite-spring</artifactId>
</dependency>

第二步,减少 ZKConfig 类,配置zookeeper

@Configuration
@ConditionalOnExpression("'${zk.serverList}'.length() > 0")
public class ZKConfig {

    @Bean
    public ZookeeperRegistryCenter registry(@Value("${zk.serverList}") String serverList,
                                             @Value("${zk.namespace}") String namespace) {return new ZookeeperRegistryCenter(new ZookeeperConfiguration(serverList, namespace));
    }

}

第三步 ,定义一个类实现SimpleJob 接口:

public class TestJob implements SimpleJob {

    @Override
    public void execute(ShardingContext shardingContext){System.out.println("ShardingTotalCount:"+shardingContext.getShardingTotalCount());
        System.out.println("ShardingItem:"+shardingContext.getShardingItem());
    }
}

第四步,减少 JobConfig 配置工作:

@Configuration
public class JobConfig {@Value("${sue.spring.elatisc.cron}")
    private String testCron;
    @Value("${sue.spring.elatisc.itemParameters}")
    private  String shardingItemParameters;
    @Value("${sue.spring.elatisc.jobParameters}")
    private String jobParameters =;
    @Value("${sue.spring.elatisc.shardingTotalCount}")
    private int shardingTotalCount;
    
    @Autowired
    private ZookeeperRegistryCenter registryCenter;

    @Bean
    public SimpleJob testJob() {return new TestJob();
    }

    @Bean
    public JobScheduler simpleJobScheduler(final SimpleJob simpleJob) {return new SpringJobScheduler(simpleJob, registryCenter, getConfiguration(simpleJob.getClass(),
                cron, shardingTotalCount, shardingItemParameters, jobParameters));
    }

    private geConfiguration getConfiguration(Class<? extends SimpleJob> jobClass,String cron,int shardingTotalCount,String shardingItemParameters,String jobParameters) {JobCoreConfiguration simpleCoreConfig = JobCoreConfiguration.newBuilder(jobClass.getName(), testCron, shardingTotalCount).
                shardingItemParameters(shardingItemParameters).jobParameter(jobParameters).build();
        SimpleJobConfiguration simpleJobConfig = new SimpleJobConfiguration(simpleCoreConfig, jobClass.getCanonicalName());
        LiteJobConfiguration jobConfig = LiteJobConfiguration.newBuilder(simpleJobConfig).overwrite(true).build();
        return jobConfig;
    }
}

其中:

  • cron:cron 表达式,定义触发规定。
  • shardingTotalCount:定义作业分片总数
  • shardingItemParameters:定义调配项参数,个别用分片序列号和参数用等号分隔,多个键值对用逗号分隔,分片序列号从 0 开始,不可大于或等于作业分片总数。
  • jobParameters:作业自定义参数

第五步 ,在applicationContext.properties 文件中配置参数:

spring.application.name=elasticjobDemo
zk.serverList=localhost:2181
zk.namespace=elasticjobDemo
sue.spring.elatisc.cron=0/5 * * * * ?
sue.spring.elatisc.itemParameters=0=A,1=B,2=C,3=D
sue.spring.elatisc.jobParameters=test
sue.spring.elatisc.shardingTotalCount=4

这样定时工作就配置好了,创立定时工作的步骤,绝对于 xxl-job 来说要繁琐一些。应用 elastic-job 的优缺点:

  • 长处:反对散布式调度协调,反对分片,适宜高并发,和一些业务相对来说较简单的场景。
  • 毛病:须要依赖于 zookeeper,实现定时工作绝对于 xxl-job 要简单一些,要对分片规定十分相熟。

3. 其余分布式定时工作

1. Saturn

Saturn 是唯品会开源的一个分布式任务调度平台。取代传统的 Linux Cron/Spring Batch Job 的形式,做到全域对立配置,对立监控,工作高可用以及分片并发解决。Saturn 是在当当开源的 Elastic-Job 根底上,联合各方需要和咱们的实际见解改进而成。应用案例:唯品会、酷狗音乐、新网银行、海融易、航美在线、量富征信等。

github 地址:https://github.com/vipshop/Sa…

2. TBSchedule

 TBSchedule 是阿里开发的一款分布式任务调度平台,旨在将调度作业从业务零碎中分离出来,升高或者是打消和业务零碎的耦合度,进行高效异步工作解决。目前被广泛应用在阿里巴巴、淘宝、支付宝、京东、聚美、汽车之家、国美等很多互联网企业的流程调度零碎中。

github 地址:https://github.com/taobao/TBS…

诚实说优良的定时工作还是挺多的,不是说哪种定时工作牛逼咱们就肯定要用哪种,而是要依据理论业务需要抉择。每种定时工作都有优缺点,正当抉择既能满足业务需要,又能防止资源节约,才是上上策。当然在理论的业务场景,通常会多种定时工作一起配合应用。

正文完
 0