使用ScheduledExecutorService代替下Timer

使用ScheduledExecutorService代替下Timer

1.scheduledExecutorService类方法scheduleAtFixedRate,ScheduleWithFixedDelay区别比较

1.1.ScheduleAtFixedRate 两次任务之间的间隔时间,取决于每次任务执行的时间长短;
假如每个任务执行花费time时间,如果time>= period,则这次任务执行结束后 立刻执行下一次任务。
如果time<period , 则这次任务执行结束后 ,隔 period-time后执行下一次任务。

1.2.ScheduleWithFixedDelay 不受任务执行时间长短影响,固定这次任务执行结束后隔x秒执行下一次.
不管任务花费多少时间,当这次任务执行结束,一定要等delay之后,再执行下一次任务。

package com.example.core.mydemo.timer;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Timer不支持并发。
 *
 * Timer不支持多线程。全部挂在Timer下的任务都是单线程的,任务仅仅能串行运行。假设当中一个任务运行时间过长。会影响到其它任务的运行,然后就可能会有各种接踵而来的问题。如果重开一个Timer?难道要为全部的耗时的Task都单开一个Timer。显然是不太可能。这样就太乱了。
 * Timer的线程不捕获异常。TimerTask假设抛出异常,那么Timer唯一的进程就会挂掉,这样挂在Timer下的全部任务都会无法继续运行
 *
 * 为了弥补Timer的缺陷,jdk1.5中引入了并发包。这里面提供的ScheduledExecutorService。详细实现类是:ScheduledThreadPoolExecutor。ScheduledThreadPoolExecutor支持多线程。同一时候在线程中对异常进行了捕获。所以是Timer的完美替换者。
 *
 * 多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用ScheduledExecutorService则没有这个问题。
 *
 */
public class TimerTest {
    private static ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(2);

    public static void main(String[] args) {
        scheduledExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("scheduledExecutorService>>submit");
            }
        });

        scheduledExecutorService.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("scheduledExecutorService>>execute");
            }
        });


        //ScheduleAtFixedRate 两次任务之间的间隔时间,取决于每次任务执行的时间长短;
        /**
         * 假如每个任务执行花费time时间,如果time>= period,则这次任务执行结束后 立刻执行下一次任务。
         * 如果time<period , 则这次任务执行结束后 ,隔 period-time后执行下一次任务。
         *
         *
         * case1:Thread.sleep(3000);
         * -----------------scheduledExecutorService>>scheduleAtFixedRate------------------
         * pool-1-thread-1begin == 1671768245503
         * pool-1-thread-1end   == 1671768248503  后4位,延时3秒
         * ------------------scheduledExecutorService>>scheduleAtFixedRate------------------
         * pool-1-thread-1begin == 1671768249503  后4位,等待4-3=1秒执行
         * pool-1-thread-1end   == 1671768252504
         *
         *case2:Thread.sleep(5000);
         * ------------------scheduledExecutorService>>scheduleAtFixedRate------------------
         * pool-1-thread-1begin == 1671768390045
         * pool-1-thread-1end   == 1671768395045    后4位,延时5秒
         * ------------------scheduledExecutorService>>scheduleAtFixedRate------------------
         * pool-1-thread-1begin == 1671768395045    后4位,立即执行
         * pool-1-thread-1end   == 1671768400045
         */
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {

                System.out.println("------------------scheduledExecutorService>>scheduleAtFixedRate------------------");
                System.out.println(Thread.currentThread().getName() + "begin == " + System.currentTimeMillis());
                try {
                    Thread.sleep(5000);  //任务花费大约3秒    >> 5000 立即执行
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "end   == " + System.currentTimeMillis());
            }
        },1,4, TimeUnit.SECONDS);


        //ScheduleWithFixedDelay 不受任务执行时间长短影响,固定这次任务执行结束后隔x秒执行下一次.
        //不管任务花费多少时间,当这次任务执行结束,一定要等delay之后,再执行下一次任务。
        /**
         * case1:Thread.sleep(3000);
         * ------------------scheduledExecutorService>>scheduleWithFixedDelay------------------
         * pool-1-thread-1begin == 1671768527054
         * pool-1-thread-1end   == 1671768530055    后4位,延时3秒
         * ------------------scheduledExecutorService>>scheduleWithFixedDelay------------------
         * pool-1-thread-1begin == 1671768534056    后4位,固定等待4秒执行
         * pool-1-thread-1end   == 1671768537056
         *
         *case2:Thread.sleep(5000);
         * ------------------scheduledExecutorService>>scheduleWithFixedDelay------------------
         * pool-1-thread-1begin == 1671768606672
         * pool-1-thread-1end   == 1671768611672    后4位,延时5秒
         * ------------------scheduledExecutorService>>scheduleWithFixedDelay------------------
         * pool-1-thread-1begin == 1671768615674    后4位,固定等待4秒执行
         * pool-1-thread-1end   == 1671768620674
         */
        scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                System.out.println("------------------scheduledExecutorService>>scheduleWithFixedDelay------------------");
                System.out.println(Thread.currentThread().getName() + "begin == " + System.currentTimeMillis());
                try{
                    Thread.sleep(5000);  //每个任务花费约3秒   >> 5000 仍然需要等待4秒执行
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "end   == " + System.currentTimeMillis());

            }
        },1,4, TimeUnit.SECONDS);



        TimerTask timerTask = new TimerTask(){
            public void run(){
                System.out.println("scheduledExecutorService>>schedule");
                this.cancel();}};

        //单次延时执行调度
        scheduledExecutorService.schedule(timerTask,1,TimeUnit.SECONDS);


        //实例化Timer类
        Timer timer=new Timer();
        timer.schedule(new TimerTask(){
        public void run(){
            System.out.println("timer>>schedule");
        this.cancel();}},1000);

    }
}

 


2.ScheduledExecutorService与Timer的对比
Timer不支持并发。
Timer不支持多线程。全部挂在Timer下的任务都是单线程的,任务仅仅能串行运行。假设当中一个任务运行时间过长。会影响到其它任务的运行,然后就可能会有各种接踵而来的问题。如果重开一个Timer?难道要为全部的耗时的Task都单开一个Timer。显然是不太可能。这样就太乱了。
Timer的线程不捕获异常。TimerTask假设抛出异常,那么Timer唯一的进程就会挂掉,这样挂在Timer下的全部任务都会无法继续运行
为了弥补Timer的缺陷,jdk1.5中引入了并发包。这里面提供的ScheduledExecutorService。详细实现类是:ScheduledThreadPoolExecutor。ScheduledThreadPoolExecutor支持多线程。同一时候在线程中对异常进行了捕获。所以是Timer的完美替换者。
多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用ScheduledExecutorService则没有这个问题。

为什么需要代替Timer:
case1、Timer管理延时任务的缺陷
   TimerThread是Thread的子类,可以看出内部只有一个线程
   符合我们的预期结果。因为ScheduledThreadPool内部是个线程池,所以可以支持多个任务并发执行


case2、Timer当任务抛出异常时的缺陷
   如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行:
   ScheduledExecutorService可以保证,task1出现异常时,不影响task2的运行


case3、Timer执行周期任务时依赖系统时间
   Timer执行周期任务时依赖系统时间,如果当前系统时间发生变化会出现一些执行上的变化,ScheduledExecutorService基于时间的延迟,不会由于系统时间的改变发生执行变化。

package com.example.core.mydemo.timer;

import java.util.Timer;
import java.util.TimerTask;

/**
 * TimerThread是Thread的子类,可以看出内部只有一个线程
 * 定义了两个任务,预计是第一个任务1s后执行,第二个任务3s后执行,但是看运行结果:
 * task1 invoked ! 1000
 * task2 invoked ! 4000
 */
public class TimerTest2 {
    private static long start;

    public static void main(String[] args) throws Exception
    {

        TimerTask task1 = new TimerTask()
        {
            @Override
            public void run()
            {
                System.out.println("task1 invoked ! " + (System.currentTimeMillis() - start));
                //case1
//                try
//                {
//                    Thread.sleep(3000);
//                } catch (InterruptedException e)
//                {
//                    e.printStackTrace();
//                }

                //case2
                //如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行:
                throw new RuntimeException();
            }
        };

        TimerTask task2 = new TimerTask()
        {
            @Override
            public void run()
            {
                System.out.println("task2 invoked ! " + (System.currentTimeMillis() - start));
            }
        };

        Timer timer = new Timer();
        start = System.currentTimeMillis();
        timer.schedule(task1, 1000);
        timer.schedule(task2, 3000);
    }

}
package com.example.core.mydemo.timer;

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

/**
 *
 * 符合我们的预期结果。因为ScheduledThreadPool内部是个线程池,所以可以支持多个任务并发执行
 * task1 invoked ! 1003
 * task2 invoked ! 3003
 */
public class ScheduledThreadPoolExecutorTest {

    private static long start;

    public static void main(String[] args)
    {
        /**
         * 使用工厂方法初始化一个ScheduledThreadPool
         */
        ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(2);

        TimerTask task1 = new TimerTask()
        {
            @Override
            public void run()
            {
                System.out.println("task1 invoked ! " + (System.currentTimeMillis() - start));
                //case1
//                try
//                {
//                    Thread.sleep(3000);
//                } catch (Exception e)
//                {
//                    e.printStackTrace();
//                }

                //case2
                //ScheduledExecutorService可以保证,task1出现异常时,不影响task2的运行
                throw new RuntimeException();

                /**
                 * case3
                 * Timer执行周期任务时依赖系统时间
                 * Timer执行周期任务时依赖系统时间,如果当前系统时间发生变化会出现一些执行上的变化,ScheduledExecutorService基于时间的延迟,不会由于系统时间的改变发生执行变化。
                 */
            }
        };

        TimerTask task2 = new TimerTask()
        {
            @Override
            public void run()
            {
                System.out.println("task2 invoked ! " + (System.currentTimeMillis() - start));
            }
        };
        start = System.currentTimeMillis();
        newScheduledThreadPool.schedule(task1, 1000, TimeUnit.MILLISECONDS);
        newScheduledThreadPool.schedule(task2, 3000, TimeUnit.MILLISECONDS);
    }

}

 





posted on 2022-12-23 14:42  oktokeep  阅读(739)  评论(4编辑  收藏  举报