SpringBoot定时任务的使用

JAVA开发领域,目前可以通过以下几种方式进行定时任务:

  • Timer:jdk中自带的一个定时调度类,可以简单的实现按某一频度进行任务执行。提供的功能比较单一,无法实现复杂的调度任务。
  • ScheduledExecutorService:也是jdk自带的一个基于线程池设计的定时任务类。其每个调度任务都会分配到线程池中的一个线程执行,所以其任务是并发执行的,互不影响。
  • Spring Task:Spring提供的一个任务调度工具,支持注解和配置文件形式,支持Cron表达式,使用简单但功能强大。
  • Quartz:一款功能强大的任务调度器,可以实现较为复杂的调度功能,如每月一号执行、每天凌晨执行、每周五执行等等,还支持分布式调度,就是配置稍显复杂。

基于JDK方式实现简单定时

刚刚有介绍过,基于JDK方式一共有两种:TimerScheduledExecutorService。接下来,就简单讲解下这两种方式。

Timer

Timer是jdk提供的java.util.Timer类。

简单示例:

    @GetMapping("/timer")
    public String doTimer() {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {

            @Override
            public void run() {
                log.info("Timer定时任务启动:" + new Date());

            }
        }, 1000,1000);//延迟1秒启动,每1秒执行一次
        return "timer";

启动后,访问即可看见控制台周期性输出信息了:

2018-08-18 21:30:35.171  INFO 13352 --- [        Timer-0] c.l.l.s.c.controller.TaskController      : Timer定时任务启动:Sat Aug 18 21:30:35 CST 2018
2018-08-18 21:30:36.173  INFO 13352 --- [        Timer-0] c.l.l.s.c.controller.TaskController      : Timer定时任务启动:Sat Aug 18 21:30:36 CST 2018
2018-08-18 21:30:37.173  INFO 13352 --- [        Timer-0] c.l.l.s.c.controller.TaskController      : Timer定时任务启动:Sat Aug 18 21:30:37 CST 2018
2018-08-18 21:30:38.173  INFO 13352 --- [        Timer-0] c.l.l.s.c.controller.TaskController      : Timer定时任务启动:Sat Aug 18 21:30:38 CST 2018
2018-08-18 21:30:39.174  INFO 13352 --- [        Timer-0] c.l.l.s.c.controller.TaskController      : Timer定时任务启动:Sat Aug 18 21:30:39 CST 2018
......

相关API简单说明:

1、在特定时间执行任务,只执行一次

public void schedule(TimerTask task,Date time)

2、在特定时间之后执行任务,只执行一次

public void schedule(TimerTask task,long delay)

3、指定第一次执行的时间,然后按照间隔时间,重复执行

public void schedule(TimerTask task,Date firstTime,long period)

4、在特定延迟之后第一次执行,然后按照间隔时间,重复执行

public void schedule(TimerTask task,long delay,long period)

5、第一次执行之后,特定频率执行,与3同

public void scheduleAtFixedRate(TimerTask task,Date firstTime,long period)

6、在delay毫秒之后第一次执行,后按照特定频率执行

public void scheduleAtFixedRate(TimerTask task,long delay,long period)

参数:

  • delay: 延迟执行的毫秒数,即在delay毫秒之后第一次执行
  • period:重复执行的时间间隔

取消任务使用:timer.cancel()方法即可注销任务。

此类相对用的较少了,简单了解下。

ScheduledExecutorService

ScheduledExecutorService可以说是Timer的替代类,因为Timer不支持多线程,任务是串行的,而且也不捕获异常,假设某个任务异常了,整个Timer就无法运行了。

简单示例:

    @GetMapping("/executor")
    public String ScheduledExecutorService() {
        //
        ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
        service.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                log.info("ScheduledExecutorService定时任务执行:" + new Date());                
            }
        }, 1, 1, TimeUnit.SECONDS);//首次延迟1秒,之后每1秒执行一次
        log.info("ScheduledExecutorService定时任务启动:" + new Date());    
        return "ScheduledExecutorService!";        
    }

启动后,可看见控制台按设定的频率输出:

2018-08-18 22:03:24.840  INFO 6752 --- [nio-8080-exec-1] c.l.l.s.c.controller.TaskController      : ScheduledExecutorService定时任务启动:Sat Aug 18 22:03:24 CST 2018
2018-08-18 22:03:25.841  INFO 6752 --- [pool-1-thread-1] c.l.l.s.c.controller.TaskController      : ScheduledExecutorService定时任务执行:Sat Aug 18 22:03:25 CST 2018
2018-08-18 22:03:26.842  INFO 6752 --- [pool-1-thread-1] c.l.l.s.c.controller.TaskController      : ScheduledExecutorService定时任务执行:Sat Aug 18 22:03:26 CST 2018
2018-08-18 22:03:27.841  INFO 6752 --- [pool-1-thread-2] c.l.l.s.c.controller.TaskController      : ScheduledExecutorService定时任务执行:Sat Aug 18 22:03:27 CST 2018
2018-08-18 22:03:28.840  INFO 6752 --- [pool-1-thread-1] c.l.l.s.c.controller.TaskController      : ScheduledExecutorService定时任务执行:Sat Aug 18 22:03:28 CST 2018
2018-08-18 22:03:29.840  INFO 6752 --- [pool-1-thread-3] c.l.l.s.c.controller.TaskController      : ScheduledExecutorService定时任务执行:Sat Aug 18 22:03:29 CST 2018

可同时设置多个任务,只需再次设置scheduleAtFixedRate即可。

常用方法说明:

  • ScheduleAtFixedRate:
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit);

参数说明:

  1. command:执行线程
  2. initialDelay:初始化延时
  3. period:两次开始执行最小间隔时间
  4. unit:计时单位
  • ScheduleWithFixedDelay:
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);

参数说明:

  1. command:执行线程
  2. initialDelay:初始化延时
  3. delay:前一次执行结束到下一次执行开始的间隔时间(间隔执行延迟时间)
  4. unit:计时单位

其他的方法大家可自行谷歌下。

基于SpingTask实现定时任务

使用SpringTaskSpringBoot是很简单的,使用@Scheduled注解即可轻松搞定。

0.启动类,加入@EnableScheduling让注解@Scheduled生效。

@SpringBootApplication
@EnableScheduling
@Slf4j
public class Chapter22Application {

    public static void main(String[] args) {
        SpringApplication.run(Chapter22Application.class, args);
        log.info("Chapter22启动!");
    }
}

1.编写一个调度类,系统启动后自动扫描,自动执行。

@Component
@Slf4j
public class ScheduledTask {

    /**
     * 自动扫描,启动时间点之后5秒执行一次
     */
    @Scheduled(fixedRate=5000)
    public void getCurrentDate() {
        log.info("Scheduled定时任务执行:" + new Date());
    }
}

2.启动后,控制台可就看见每5秒一次输出了:

2018-08-18 22:23:09.735  INFO 13812 --- [pool-1-thread-1] c.l.l.s.c.controller.ScheduledTask       : Scheduled定时任务执行:Sat Aug 18 22:23:09 CST 2018
2018-08-18 22:23:14.734  INFO 13812 --- [pool-1-thread-1] c.l.l.s.c.controller.ScheduledTask       : Scheduled定时任务执行:Sat Aug 18 22:23:14 CST 2018
2018-08-18 22:23:19.735  INFO 13812 --- [pool-1-thread-1] c.l.l.s.c.controller.ScheduledTask       : Scheduled定时任务执行:Sat Aug 18 22:23:19 CST 2018
2018-08-18 22:23:24.735  INFO 13812 --- [pool-1-thread-1] c.l.l.s.c.controller.ScheduledTask       : Scheduled定时任务执行:Sat Aug 18 22:23:24 CST 2018
2018-08-18 22:23:29.735  INFO 13812 --- [pool-1-thread-1] c.l.l.s.c.controller.ScheduledTask       : Scheduled定时任务执行:Sat Aug 18 22:23:29 CST 2018
......

使用都是简单的,现在我们来看看注解@Scheduled的参数意思:

  1. fixedRate:定义一个按一定频率执行的定时任务
  2. fixedDelay:定义一个按一定频率执行的定时任务,与上面不同的是,改属性可以配合initialDelay, 定义该任务延迟执行时间。
  3. cron:通过表达式来配置任务执行时间

Cron表达式详解

一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素。

依次顺序如下表所示:

字段 允许值 允许的特殊字符
0~59 , - * /
0~59 , - * /
小时 0~23 , - * /
日期 1-31 , - * ? / L W C
月份 112或者JANDEC , - * /
星期 17或者SUNSAT , - * ? / L C #
年(可选) 留空,1970~2099 , - * /

简单举例:

  • 0/1 * * * * ?:每秒执行一次
  • 0 0 2 1 * ? : 表示在每月的1日的凌晨2点调整任务
  • 0 0 10,14,16 ? :每天上午10点,下午2点,4点
  • 0 0 12 * * ? : 每天中午12点触发
  • 0 15 10 ? * MON-FRI : 周一至周五的上午10:15触发

更多表达式,可访问:http://cron.qqe2.com/ 进行在线表达式编写。简单明了。

在线表达式编辑器

自定义线程池

从控制台输出可以看见,多任务使用的是同一个线程。可结合上章节的异步调用来实现不同任务使用不同的线程进行任务执行。

0.编写配置类,同时启用@Async注解:

@Configuration
@EnableAsync
public class Config {
    /**
     * 配置线程池
     * @return
     */
    @Bean(name = "scheduledPoolTaskExecutor")
    public ThreadPoolTaskExecutor getAsyncThreadPoolTaskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(20);
        taskExecutor.setMaxPoolSize(200);
        taskExecutor.setQueueCapacity(25);
        taskExecutor.setKeepAliveSeconds(200);
        taskExecutor.setThreadNamePrefix("oKong-Scheduled-");
        // 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //调度器shutdown被调用时等待当前被调度的任务完成
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        //等待时长
        taskExecutor.setAwaitTerminationSeconds(60);
        taskExecutor.initialize();
        return taskExecutor;
    }
}

1.调度类上加入@Async

@Component
@Slf4j
public class ScheduledTask {

    /**
     * 自动扫描,启动时间点之后5秒执行一次
     */
    @Async("scheduledPoolTaskExecutor")
    @Scheduled(fixedRate=5000)
    public void getCurrentDate() {
        log.info("Scheduled定时任务执行:" + new Date());
    }
}

再次启动程序,可看见控制台输出,任务已经是不同线程下执行了:

2018-08-18 22:47:13.313  INFO 14212 --- [ong-Scheduled-1] c.l.l.s.c.controller.ScheduledTask       : Scheduled定时任务执行:Sat Aug 18 22:47:13 CST 2018
2018-08-18 22:47:13.343  INFO 14212 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2018-08-18 22:47:13.348  INFO 14212 --- [           main] c.l.l.s.chapter22.Chapter22Application   : Started Chapter22Application in 2.057 seconds (JVM running for 2.855)
2018-08-18 22:47:13.348  INFO 14212 --- [           main] c.l.l.s.chapter22.Chapter22Application   : Chapter22启动!
2018-08-18 22:47:18.308  INFO 14212 --- [ong-Scheduled-2] c.l.l.s.c.controller.ScheduledTask       : Scheduled定时任务执行:Sat Aug 18 22:47:18 CST 2018

动态添加定时任务

使用注解的方式,无法实现动态的修改或者添加新的定时任务的,这个使用就需要使用编程的方式进行任务的更新操作了。可直接使用ThreadPoolTaskScheduler或者SchedulingConfigurer接口进行自定义定时任务创建。

ThreadPoolTaskScheduler

ThreadPoolTaskSchedulerSpringTask的核心实现类,该类提供了大量的重载方法进行任务调度。这里简单示例下,具体的大家自行搜索下,用的少不太了解呀。

0.创建一个ThreadPoolTaskScheduler类。

    @Bean("taskExecutor")
    public TaskScheduler taskExecutor() {
        ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
        executor.setPoolSize(20);
        executor.setThreadNamePrefix("oKong-taskExecutor-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //调度器shutdown被调用时等待当前被调度的任务完成
        executor.setWaitForTasksToCompleteOnShutdown(true);
        //等待时长
        executor.setAwaitTerminationSeconds(60);
        return executor;
    }

1.编写一个控制类,动态设置定时任务:

    @Autowired
    TaskScheduler taskScheduler;

    @GetMapping("/poolTask")
    public String threadPoolTaskScheduler() {

        taskScheduler.schedule(new Runnable() {

            @Override
            public void run() {
                log.info("ThreadPoolTaskScheduler定时任务:" + new Date());
            }
        }, new CronTrigger("0/3 * * * * ?"));//每3秒执行一次
        return "ThreadPoolTaskScheduler!";
    }

2.启动后,访问接口,即可看见控制台每3秒输出一次:

2018-08-18 23:20:39.002  INFO 9120 --- [Kong-Executor-1] c.l.l.s.c.controller.TaskController      : ThreadPoolTaskScheduler定时任务:Sat Aug 18 23:20:39 CST 2018
2018-08-18 23:20:42.000  INFO 9120 --- [Kong-Executor-1] c.l.l.s.c.controller.TaskController      : ThreadPoolTaskScheduler定时任务:Sat Aug 18 23:20:42 CST 2018
2018-08-18 23:20:45.002  INFO 9120 --- [Kong-Executor-2] c.l.l.s.c.controller.TaskController      : ThreadPoolTaskScheduler定时任务:Sat Aug 18 23:20:45 CST 2018
2018-08-18 23:20:48.001  INFO 9120 --- [Kong-Executor-1] c.l.l.s.c.controller.TaskController      : ThreadPoolTaskScheduler定时任务:Sat Aug 18 23:20:48 CST 2018

SchedulingConfigurer

此类十个接口,直接实现其configurerTasks方法即可。

0.编写配置类:

@Configuration
@Slf4j
public class ScheduleConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setTaskScheduler(taskExecutor());
        taskRegistrar.getScheduler().schedule(new Runnable() {

            @Override
            public void run() {
                log.info("SchedulingConfigurer定时任务:" + new Date());
            }
        }, new CronTrigger("0/3 * * * * ?"));//每3秒执行一次
    }

    @Bean("taskExecutor")
    public TaskScheduler taskExecutor() {
        ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
        executor.setPoolSize(20);
        executor.setThreadNamePrefix("oKong-Executor-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //调度器shutdown被调用时等待当前被调度的任务完成
        executor.setWaitForTasksToCompleteOnShutdown(true);
        //等待时长
        executor.setAwaitTerminationSeconds(60);
        return executor;
    }

}

1.启动后,控制台也可以看见每3秒输出一次:

2018-08-18 23:24:39.001  INFO 868 --- [Kong-Executor-1] c.l.l.s.chapter22.config.ScheduleConfig  : SchedulingConfigurer定时任务:Sat Aug 18 23:24:39 CST 2018
2018-08-18 23:24:42.001  INFO 868 --- [Kong-Executor-1] c.l.l.s.chapter22.config.ScheduleConfig  : SchedulingConfigurer定时任务:Sat Aug 18 23:24:42 CST 2018
2018-08-18 23:24:45.000  INFO 868 --- [Kong-Executor-2] c.l.l.s.chapter22.config.ScheduleConfig  : SchedulingConfigurer定时任务:Sat Aug 18 23:24:45 CST 2018

基于Quartz实现定时调度

由于本章节是基于SpringBoot 1.x版本的,所以没有基于Quartzstarter配置,这里直接引入了Quartz相关依赖包来集成。

0.加入pom依赖

    <!-- quartz -->
    <dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz</artifactId>
        <version>2.2.3</version>
    </dependency>
    <!-- spring集成quartz -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
    </dependency>
    <!-- 因为SchedulerFactoryBean中依赖了org.springframework.transaction.PlatformTransactionManager,所以需依赖tx相关包,其实还是quartz有个分布式功能,是使用数据库完成的。 -->
        <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
    </dependency>

1.编写配置类。

@Configuration
@Slf4j
public class QuartzConfig {

    /**
     * 通过工厂类,创建job实例
     * @return
     */
    @Bean
    public MethodInvokingJobDetailFactoryBean customJobDetailFactoryBean() {

        MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean();
        //设置执行任务的bean
        jobDetail.setTargetBeanName("quartzTask");
        //设置具体执行的方法
        jobDetail.setTargetMethod("quartzTask");
        //同步执行,上一任务未执行完,下一任务等待
        //true 任务并发执行
        //false 下一个任务必须等待上一任务完成
        jobDetail.setConcurrent(false);
        return jobDetail; 
    }

    /**
     * 通过工厂类创建Trigger
     * @param jobDetailFactoryBean
     * @return
     * @throws ParseException 
     */
    @Bean(name = "cronTriggerBean")
    public Trigger cronTriggerBean(MethodInvokingJobDetailFactoryBean jobDetailFactoryBean) throws ParseException {
        CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
        cronTriggerFactoryBean.setJobDetail(jobDetailFactoryBean.getObject());
        cronTriggerFactoryBean.setCronExpression("0/3 * * * * ?");//每3秒执行一次
        cronTriggerFactoryBean.setName("customCronTrigger");
        cronTriggerFactoryBean.afterPropertiesSet();
        return cronTriggerFactoryBean.getObject();

    }

    /**
     * 调度工厂类,自动注入Trigger
     * @return
     */
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(Trigger... triggers) {
        SchedulerFactoryBean bean = new SchedulerFactoryBean();

        //也可以直接注入 ApplicationContext,利于 getBeansOfType获取trigger
//        Map<String,Trigger> triggerMap = appContext.getBeansOfType(Trigger.class);
//        if(triggerMap != null) {
//            List<Trigger> triggers = new ArrayList<>(triggerMap.size());
//            //
//            triggerMap.forEach((key,trigger)->{
//                triggers.add(trigger);
//            });
//            bean.setTriggers(triggers.toArray(new Trigger[triggers.size()]));
//        }
        //这里注意 对应的trigger 不能为null 不然会异常的
        bean.setTriggers(triggers);
        return bean;
    } 

    @Component("quartzTask")
    public class QuartzTask {

        public void quartzTask() {
            log.info("Quartz定时任务:" + new Date());
        }
    }
}

2.启动后,可以看见控制台以每3秒执行一次输出:

2018-08-18 23:42:03.019  INFO 772 --- [ryBean_Worker-2] c.l.l.s.chapter22.config.QuartzConfig    : Quartz定时任务:Sun Aug 18 23:42:03 CST 2018
2018-08-18 23:42:06.002  INFO 772 --- [ryBean_Worker-3] c.l.l.s.chapter22.config.QuartzConfig    : Quartz定时任务:Sun Aug 18 23:42:06 CST 2018
2018-08-18 23:42:09.002  INFO 772 --- [ryBean_Worker-4] c.l.l.s.chapter22.config.QuartzConfig    : Quartz定时任务:Sun Aug 18 23:42:09 CST 2018

参考资料

  1. https://www.cnblogs.com/yank/p/3955322.html
  2. https://blog.csdn.net/tsyj810883979/article/details/8481621
  3. https://www.cnblogs.com/javahr/p/8318728.html
  4. http://www.quartz-scheduler.org/documentation/quartz-2.1.x/quick-start.html
  5. https://spring.io/guides/gs/scheduling-tasks/
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容

  • 一点知识 在JAVA开发领域,目前可以通过以下几种方式进行定时任务: Timer:jdk中自带的一个定时调度类,可...
    雅倩兰爸爸阅读 3,827评论 1 28
  • 前言 任务调度是指基于给定时间点,给定时间间隔或者给定执行次数自动执行任务。本文由浅入深介绍四种任务调度的 Jav...
    Java高级架构狮阅读 527评论 0 2
  • 博客原文 徒手翻译spring framework 4.2.3官方文档的第33章,若有翻译不当之处请指正。 定时任...
    rabbitGYK阅读 5,614评论 4 24
  • 每当黑夜来临 小巷热闹起来 白天因为巷黑 少有人走巷道 按照常理来说 夜晚人会更少 可事实却不然 或许正是太黑 有...
    刘绍迪阅读 995评论 0 1
  • 在晏知还八岁的时候,就已经开始在这一带捡垃圾了,附近的人都知道有一个小乞丐一到半夜就开始翻垃圾箱。刚开始晏知还只是...
    Bubblue阅读 495评论 0 0