Java并发之定时任务接口ScheduledExecutorService

来源:互联网 发布:计算机算法设计 网课 编辑:程序博客网 时间:2024/05/21 05:18

以前在使用Timer作为定时任务时,如果同时执行多个爬虫,且执行的时间过长,超过了两个爬虫的间隔时间,就会出现一些奇怪的错误,在查找了一些资料之后,这次来尝试下ScheduledExecutorService做为定时器,下面是简单的介绍。


java中要实现定时执行的方法通常有三种(这里不包括spring和一些已经实现好的工具类):

  • 第一种就是创建一个线程然后一直循环通过sleep来达到定时执行,但是不容易控制任务的启动和取消
  • 第二种就是使用Timer和TimerTask,这个使用起来相比第一种好了很多,但是做为单线程在处理多个任务和抛出异常的时候就不那么方便了
  • 第三种ScheduledExecutorService是通常最理想的定时任务实现方式,主要将定时任务和线程池功能结合,能够更好的处理java中的一些并发的任务需要。

ScheduledExecutorService,是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行,也就是说,任务是并发执行,互不影响。需要注意,只有当调度任务来的时候,ScheduledExecutorService才会真正启动一个线程,其余时间ScheduledExecutorService都是出于轮询任务的状态。

首先是一个简单的例子:

import java.util.concurrent.Executors;  import java.util.concurrent.ScheduledExecutorService;  import java.util.concurrent.TimeUnit;  public class Test{      public static void main(String[] args) {          Runnable runnable = new Runnable() {          //所要定时执行的任务            public void run() {                 System.out.println("Hello !!");              }          };          ScheduledExecutorService service = Executors                  .newSingleThreadScheduledExecutor();          // 第二个参数为首次执行的延时时间,第三个参数为定时执行的间隔时间(下面有解释这两种方法的区别)          service.scheduleAtFixedRate(runnable, 10, 1, TimeUnit.SECONDS);        servuce.scheduleWithFixedDelay(runnable, 10, 1,TimeUnit.SECONDS);      }  }  

这里创建任务是使用Runnable接口,在程序开发中只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下好处:

  • 避免点继承的局限,一个类可以继承多个接口。
  • 适合于资源的共享

方法介绍:

其中scheduleAtFixedRate(TimerTask task,long delay,long period) 方法用于安排指定的任务进行重复的固定速率执行,在指定的延迟后开始。参数说明如下:

  • task:这是要被调度的任务
  • delay:这是以毫秒为单位的延迟之后的任务执行
  • period:这是在连续执行任务之间的毫秒的时间

在执行这个方法的时候会有两个异常要考虑:

  • IllegalArgumentException–这个异常被抛出,如果time.getTime()为负。
  • IllegalStateException–这将被抛出,如果任务已经安排或取消,计时器被取消,或者计时器线程已终止。

两种方法的区别:

ScheduledExecutorService 中两种最常用的调度方法 ScheduleAtFixedRate 和 ScheduleWithFixedDelay。

  • ScheduleAtFixedRate 每次执行时间为上一次任务开始起向后推一个时间间隔,即每次执行时间为 :initialDelay,
    initialDelay+period, initialDelay+2*period, …;

  • ScheduleWithFixedDelay每次执行时间为上一次任务结束起向后推一个时间间隔,即每次执行时间为:initialDelay,
    initialDelay+executeTime+delay, initialDelay+2*executeTime+2*delay。

由此可见,ScheduleAtFixedRate 是基于固定时间间隔进行任务调度,ScheduleWithFixedDelay 取决于每次任务执行的时间长短,是基于不固定时间间隔进行任务调度


程序有时候要求任务在每天的某一个时刻运行,下面是我自己写的一个列子,供参考:

import java.text.ParseException;import java.util.Date;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;/** * Created by gray- on 2017/10/25. * * * 测试java中并发的ScheduledExecutorService 的定时效果 * * * 它具有以下的好处: * 1、相比于Timer的单线程,它是通过线程池的方式来执行任务的 * 2、可以很灵活的去设定第一次执行任务delay的时间 * 3、提供了更好的约定,以便设定执行的时间间隔 */public class ScheduledExecutorServiceTest {    public static void main(String[] args){        Runnable runnable = new Runnable() {            @Override            public void run() {                System.out.println("hello!!!");            }        };        //在每天的下午五点半运行        long oneDay = 60 * 60 * 24 * 1000;        long initDelay = getTimeMillis("17:30:00") - System.currentTimeMillis();        initDelay = initDelay>0? initDelay : oneDay + initDelay;        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();        service.scheduleAtFixedRate(runnable, initDelay , oneDay, TimeUnit.MILLISECONDS);    }    public static long getTimeMillis(String time){        DateFormat dateFormat = new SimpleDateFormat("yy-MM-dd HH:mm:ss");        DateFormat dayFormat = new SimpleDateFormat("yy-MM-dd");        try {            Date curDate = dateFormat.parse(dayFormat.format(new Date()) + " " + time);            return curDate.getTime();        } catch (ParseException e) {            e.printStackTrace();        }        return 0;    }}

以上就是对ScheduledExecutorService的简单的了解,要想深入理解可以直接看源码,
这里分享一篇介绍源码的文章:http://www.cnblogs.com/sunxucool/archive/2013/06/21/3148141.html

阅读全文
1 0
原创粉丝点击