美文网首页Android开发程序员
Java定时线程池_ScheduledExecutorServi

Java定时线程池_ScheduledExecutorServi

作者: wo883721 | 来源:发表于2018-04-13 10:44 被阅读78次

    在上一章中,我们详细讲解线程池的作用,它可以在新的线程中执行任务,而且高效快捷,因为不用频繁地创建和销毁线程。
    但是我们经常碰到这样的需求,任务不是立即执行,而是经过一段时间之后,才会执行。或者任务可以循环周期性地执行。那么怎么实现这样的需求呢?
    在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接口中的方法已经详细举例分析了,如果你只是想使用定时线程池的话,那么你已经知道该如何使用了。但是如果你想知道定时线程池是怎么实现的话,请看我的下一篇文章。

    相关文章

      网友评论

        本文标题:Java定时线程池_ScheduledExecutorServi

        本文链接:https://www.haomeiwen.com/subject/iagqkftx.html