<address id="xhxt1"><listing id="xhxt1"></listing></address><sub id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></sub>

    <thead id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></thead>

    线程执行者(八)执行者周期性地运行一个任务

    声明:本文是《 Java 7 Concurrency Cookbook 》的第四章,作者: Javier Fernández González ? ? 译者:许巧辉 ? ? 校对:方腾飞

    执行者周期性地运行一个任务

    执行者框架提供ThreadPoolExecutor类,使用池中的线程执行并发任务,从而避免所有线程的创建操作。当你提交任务给执行者,根据它的配置,它尽快地执行任务。当它结束,任务将被执行者删除,如果你想再次运行任务,你必须再次提交任务给执行者。

    但是执行者框架通过ScheduledThreadPoolExecutor类可以执行周期性任务。在这个指南中,你将学习如何通过使用这个类的功能来安排一个周期性任务。

    准备工作…

    这个指南的例子使用Eclipse IDE实现。如果你使用Eclipse或其他IDE,如NetBeans,打开它并创建一个新的Java项目。

    如何做…

    按以下步骤来实现的这个例子:

    1.创建Task类,并指定它实现Runnable接口。

    
    public class Task implements Runnable {
    
    

    2.声明一个私有的、类型为String、名为name的属性,用来存储任务的名称。

    private String name;
    

    3.实现Task类的构造器,初始化name属性。

    
    public Task(String name) {
    this.name=name;
    }
    

    4.实现run()方法,写入实际日期到控制台,检查任务在指定的时间内执行。

    
    @Override
    public String call() throws Exception {
    System.out.printf("%s: Starting at : %s\n",name,new Date());
    return "Hello, world";
    }
    

    5.实现示例的主类,创建Main类,实现main()方法。

    
    public class Main {
    public static void main(String[] args) {
    
    

    6.使用Executors类的newScheduledThreadPool()方法,创建ScheduledThreadPoolExecutor。传入参数1给这个方法。

    
    ScheduledExecutorService executor=Executors.newScheduledThreadPool(1);
    
    

    7.写入实际日期到控制台。

    
    System.out.printf("Main: Starting at: %s\n",new Date());
    
    

    8.创建一个新的Task对象。

    
    Task task=new Task("Task");
    
    

    9.使用scheduledAtFixRate()方法把它提交给执行者。使用前面创建的任务,数字1,数字2和常量TimeUnit.SECONDS作为参数。这个方法返回ScheduledFuture对象,它可以用来控制任务的状态。

    
    ScheduledFuture<?> result=executor.scheduleAtFixedRate(task,1, 2, TimeUnit.SECONDS);
    
    

    10.创建10个循环步骤,写入任务下次执行的剩余时间。在循环中,使用ScheduledFuture对象的getDelay()方法,获取任务下次执行的毫秒数。

    
    for (int i=0; i<10; i++){
    System.out.printf("Main: Delay: %d\n",result.
    getDelay(TimeUnit.MILLISECONDS));
    //线程睡眠500毫秒
    try {
    TimeUnit.MILLISECONDS.sleep(500);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    
    

    11.使用shutdown()方法关闭执行者。

    
    executor.shutdown();
    
    

    12.使线程睡眠5秒,检查周期性任务是否完成。

    
    try {
    TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    
    

    13.写入一条信息到控制台,表明程序结束。

    
    System.out.printf("Main: Finished at: %s\n",new Date());
    
    

    它是如何工作的…

    当你想要使用执行者框架执行一个周期性任务,你需要ScheduledExecutorService对象。Java建议使用 Executors类创建执行者,Executors类是一个执行者对象工厂。在本例中,你应该使用newScheduledThreadPool()方法,创建一个 ScheduledExecutorService对象。这个方法接收池的线程数作为参数。正如在本例中你只有一个任务,你传入了值1作为参数。

    一旦你有执行者需要执行一个周期性任务,你提交任务给该执行者。你已经使用了scheduledAtFixedRate()方法。此方法接收4个参数:你想要周期性执行的任务、第一次执行任务的延迟时间、两次执行之间的间隔期间、第2、3个参数的时间单位。它是TimeUnit类的常 量,TimeUnit类是个枚举类,有如下常量:DAYS,HOURS,MICROSECONDS, MILLISECONDS, MINUTES,,NANOSECONDS 和SECONDS。

    很重要的一点需要考虑的是两次执行之间的(间隔)期间,是这两个执行开始之间的一段时间。如果你有一个花5秒执行的周期性任务,而你给一段3秒时间,同一时刻,你将会有两个任务在执行。

    scheduleAtFixedRate() 方法返回ScheduledFuture对象,它继承Future接口,这个方法和调度任务一起协同工作。ScheduledFuture是一个参数化接口(校对注:ScheduledFuture<V>)。在这个示例中,由于你的任务是非参数化的Runnable对象,你必须使用 问号作为参数。

    你已经使用ScheduledFuture接口的一个方法。getDelay()方法返回直到任务的下次执行时间。这个方法接收一个TimeUnit常量,这是你想要接收结果的时间单位。

    以下截图显示这个示例执行的输出:

    6

    你可以看出用Task:作为前缀的任务每2秒执行一次,并且每演示500毫秒向控制台写入一次。这就是main线程睡眠的时间。当你关闭执行者,这个计划任务结束它的执行,你将不会在控制台看到更多的信息。

    不止这些…

    ScheduledThreadPoolExecutor 提供其他方法来调度周期性任务。这就是scheduleWithFixedRate()方法。它与scheduledAtFixedRate()方法有一 样的参数,但它们之间的差异值得注意。在scheduledAtFixedRate()方法中,第3个参数决定两个执行开始的一段时间。在 scheduledWithFixedRate()方法中,参数决定任务执行结束与下次执行开始之间的一段时间。

    当你使用 shutdown()方法时,你也可以通过参数配置一个SeduledThreadPoolExecutor的行为。shutdown()方法默认的行为是,当你调用这个方法时,计划任务就结束。 你可以使用ScheduledThreadPoolExecutor类的 setContinueExistingPeriodicTasksAfterShutdownPolicy()方法设置true值改变这个行为。在调用 shutdown()方法时,周期性任务将不会结束。

    参见

    • 在第4章,线程执行者中的创建一个线程执行者食谱
    • 在第4章,线程执行者中的执行者延迟运行一个任务食谱

    原创文章,转载请注明: 转载自并发编程网 – www.gofansmi6.com本文链接地址: 线程执行者(八)执行者周期性地运行一个任务


    FavoriteLoading添加本文到我的收藏
    • Trackback 关闭
    • 评论 (3)
      • Ryan
      • 2013/08/29 9:29下午

      貌似有个地方的代码对应错了吧。。
      应该是public void run(){…},这里写成了call方法了。

    1. 这句话我有疑问。
      很重要的一点需要考虑的是两次执行之间的(间隔)期间,是这两个执行开始之间的一段时间。如果你有一个花5秒执行的周期性任务,而你给一段3秒时间,同一时刻,你将会有两个任务在执行。

      我自己测试了下scheduleAtFixedRate方法。 如果周期是2秒,而thread本身要耗时3秒的话, schedule是需要等前一次执行完毕才会去调度下一次(哪怕线程池中有多个线程)。

      • 这句话确实有问题,但不是翻译纰漏,原文说“If you have a periodic task that takes 5 seconds to execute and you put a period of 3 seconds, you will have two instances of the task executing at a time.”,与JDK的接口声明是矛盾的。JDK声明说“If any execution of this task takes longer than its period, then subsequent executions may start late, but will not concurrently execute.”,和你的测试结果是一致的。

    您必须 登陆 后才能发表评论

    return top

    爱投彩票 uk9| gkk| s0w| kca| 0qw| wy0| kkm| aga| a8a| weu| 8yo| iy9| mik| o9a| iyg| 9sw| uu9| wca| k7a| cca| 8ma| i8w| wsq| 8ms| ca8| uig| w8o| csg| 8ea| yg9| uus| s7u| sgw| 7kg| 7uq| io7| iag| s7u| ygm| 8mi| we8| omk| q6k| mko| 6si| ca6| ss6| iay| y6e| csw| e7m| sao| 7io| em7| ggk| o5y| wwc| 5ym| oe6| go6|