Android开发程序员

Java定时线程池_ScheduledExecutorServi

2018-04-13  本文已影响78人  wo883721

在上一章中,我们详细讲解线程池的作用,它可以在新的线程中执行任务,而且高效快捷,因为不用频繁地创建和销毁线程。
但是我们经常碰到这样的需求,任务不是立即执行,而是经过一段时间之后,才会执行。或者任务可以循环周期性地执行。那么怎么实现这样的需求呢?
在java中提供了ScheduledExecutorService接口,那么实现上面的需求。

一. ScheduledExecutorService接口

public interface ScheduledExecutorService extends ExecutorService {


    // 给定的延迟时间delay之后,才会执行任务command
    public ScheduledFuture<?> schedule(Runnable command,
                                       long delay, TimeUnit unit);


    // 给定的延迟时间delay之后,才会执行任务callable
    public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                           long delay, TimeUnit unit);

    /**
     * 在给定的初始化延时initialDelay之后,固定频率地周期性执行任务command。
     * 也就是说任务第一次运行时间是initialDelay,第二次运行时间是initialDelay+period,
     * 第三次是initialDelay + period*2等等。 所以频率是相同地。
     *
     * 但是有一个问题,如果任务运行时间大于周期时间period该怎么办?
     * 其实是这样的,在initialDelay之后开始运行任务,当任务完成之后,
     * 将当前时间与initialDelay+period时间进行比较,如果小于initialDelay+period时间,那么等待,
     * 如果大于initialDelay+period时间,那么就直接执行第二次任务
     *
     */
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);


    /**
     * 在给定的初始化延时initialDelay之后,开始执行任务,任务执行完成之后,
     * 等待delay时间,再一次执行任务。
     * 因为它是等待任务完成之后,再进行延迟,就不会受任务完成时间长短地影响。
     */
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);

}

ScheduledExecutorService接口定义了四个方法:

  1. schedule的两个方法,延时delay时间后执行任务。区别Callable参数可以获取任务完成的结果值,Runnable参数的不会得到结果值。
  2. scheduleAtFixedRate方法:在延时initialDelay时间之后,开始第一次执行任务,然后每隔周期时间period,再次执行任务。注意如果任务消耗时间大于周期时间period,会等待任务完成之后,才再次执行任务。
  3. scheduleWithFixedDelay方法:在延时initialDelay时间之后,开始第一次执行任务,任务执行完成之后,再延时delay时间,然后再次执行任务。

下面我们结合例子来说明。

二. 延迟执行任务

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

class MyRun implements Runnable {

    private long start;

    public MyRun() {
        this.start = System.currentTimeMillis();
    }

    @Override
    public void run() {
        long time = System.currentTimeMillis() - start;
        System.out.println("--"+Thread.currentThread().getName()+"开始运行  time=="+time);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName()+" 发生中断异常  exception=="+e.getMessage());
        }
        time = System.currentTimeMillis() - start;
        System.out.println("======="+Thread.currentThread().getName()+"结束          time=="+time);
    }
}

class MThreadFactory implements ThreadFactory {
    private int sequenceNumber = 0;

    @Override
    public Thread newThread(Runnable r) {
        return new Thread(r, "线程"+(++sequenceNumber));
    }
}
public class ScheduledExecutorServiceTest {
    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(new MThreadFactory());
        MyRun run = new MyRun();
        service.schedule(run, 1000, TimeUnit.MILLISECONDS);
    }
}

运行结果:

--线程1开始运行  time==1004
=======线程1结束          time==2006

可以看出任务run的确延时了1秒之后才执行的。

三. 周期性执行任务

3.1 scheduleAtFixedRate方法

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

class MyRun implements Runnable {

    private long start;

    public MyRun() {
        this.start = System.currentTimeMillis();
    }

    @Override
    public void run() {
        long time = System.currentTimeMillis() - start;
        System.out.println("--"+Thread.currentThread().getName()+"开始运行  time=="+time);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName()+" 发生中断异常  exception=="+e.getMessage());
        }
        time = System.currentTimeMillis() - start;
        System.out.println("======="+Thread.currentThread().getName()+"结束          time=="+time);
    }
}

class MThreadFactory implements ThreadFactory {
    private int sequenceNumber = 0;

    @Override
    public Thread newThread(Runnable r) {
        return new Thread(r, "线程"+(++sequenceNumber));
    }
}
public class ScheduledExecutorServiceTest {
    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(new MThreadFactory());
        MyRun run = new MyRun();
        service.scheduleAtFixedRate(run, 0, 2000, TimeUnit.MILLISECONDS);
    }
}

运行结果:

--线程1开始运行  time==1
=======线程1结束          time==1005
--线程1开始运行  time==2005
=======线程1结束          time==3009
--线程1开始运行  time==4003
=======线程1结束          time==5007
--线程1开始运行  time==6002
=======线程1结束          time==7007
--线程1开始运行  time==8004
=======线程1结束          time==9005
--线程1开始运行  time==10004
=======线程1结束          time==11008

调用了scheduleAtFixedRate方法,设置周期时间是2000毫秒,发现任务run的确是每隔2000毫秒执行一次。那是因为我们任务消耗时间是1000毫秒,小于周期时间。如果我们将周期时间改为500毫秒会出现什么情况?
运行结果:

--线程1开始运行  time==1
=======线程1结束          time==1005
--线程1开始运行  time==1006
=======线程1结束          time==2011
--线程1开始运行  time==2011
=======线程1结束          time==3015
--线程1开始运行  time==3015
=======线程1结束          time==4019
--线程1开始运行  time==4019
=======线程1结束          time==5023
--线程1开始运行  time==5024
=======线程1结束          time==6028
--线程1开始运行  time==6028
=======线程1结束          time==7032

我们发现任务run并不是每隔周期时间500毫秒执行一次,而是每隔任务完成时间1000毫秒执行一次.

所以对于scheduleAtFixedRate方法来说:

  1. 如果执行完任务后,发现时间没有到定时的周期时间,那么就会等待,直到时间到了再执行任务。
  2. 如果执行完任务后,发现时间超过定时的周期时间,那么就直接再次执行任务。

3.2 scheduleWithFixedDelay方法

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

class MyRun implements Runnable {

    private long start;

    public MyRun() {
        this.start = System.currentTimeMillis();
    }

    @Override
    public void run() {
        long time = System.currentTimeMillis() - start;
        System.out.println("--"+Thread.currentThread().getName()+"开始运行  time=="+time);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName()+" 发生中断异常  exception=="+e.getMessage());
        }
        time = System.currentTimeMillis() - start;
        System.out.println("======="+Thread.currentThread().getName()+"结束          time=="+time);
    }
}

class MThreadFactory implements ThreadFactory {
    private int sequenceNumber = 0;

    @Override
    public Thread newThread(Runnable r) {
        return new Thread(r, "线程"+(++sequenceNumber));
    }
}
public class ScheduledExecutorServiceTest {
    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(new MThreadFactory());
        MyRun run = new MyRun();
        service.scheduleWithFixedDelay(run, 0, 500, TimeUnit.MILLISECONDS);
    }
}

运行结果:

--线程1开始运行  time==1
=======线程1结束          time==1004
--线程1开始运行  time==1509
=======线程1结束          time==2513
--线程1开始运行  time==3013
=======线程1结束          time==4016
--线程1开始运行  time==4519
=======线程1结束          time==5524
--线程1开始运行  time==6028
=======线程1结束          time==7029
--线程1开始运行  time==7531
=======线程1结束          time==8535
--线程1开始运行  time==9040
=======线程1结束          time==10044

scheduleWithFixedDelay方法的意义就是当任务完成之后,延迟delay时间,再一次执行任务,一直循环下去。

总结

ScheduledExecutorService接口中的方法已经详细举例分析了,如果你只是想使用定时线程池的话,那么你已经知道该如何使用了。但是如果你想知道定时线程池是怎么实现的话,请看我的下一篇文章。

上一篇 下一篇

猜你喜欢

热点阅读