• 定时任务实现方式对比


    1. 定时任务实现方式对比

    1.1. Timer

    • 代码例子如下
        public static void main(String[] args) {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            LocalDateTime localDateTime = LocalDateTime.now();
            String format = localDateTime.format(formatter);
            System.out.println("1:"+format);
            Timer timer = new Timer();
            for (int i = 0; i < 2; i++) {
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("Thread name : "+ Thread.currentThread().getName());
                        LocalDateTime localDateTime = LocalDateTime.now();
                        String format = localDateTime.format(formatter);
                        System.out.println("2:"+format);
                    }
                }, 3000);
            }
    
            localDateTime = LocalDateTime.now();
            format = localDateTime.format(formatter);
            System.out.println("3:"+format);
        }
    

    结果

    1:2019-10-14 17:35:13
    3:2019-10-14 17:35:13
    Thread name : Timer-0
    2:2019-10-14 17:35:19
    Thread name : Timer-0
    2:2019-10-14 17:35:22
    
    • 可以看出同一个Timer的定时任务,后台就一个线程管理任务分配,遇到任务阻塞,可能导致下一个任务延迟
    • 且如果任务发生异常,系统就终止了

    1.2. ScheduledExecutorService

    • 为了解决Timer的问题,ScheduledExecutorService做了改进,采用了线程池的定时任务队列,实际使用的也是最小堆排序
    • 代码如下
    private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        public static void main(String[] args) {
    //        timerTest();
            print("1:");
            ScheduledExecutorService service = new ScheduledThreadPoolExecutor(2);
            for (int i = 0; i < 2; i++) {
                service.schedule(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("Thread name : " + Thread.currentThread().getName());
                        print("2:");
                    }
                }, 3, TimeUnit.SECONDS);
            }
            print("3:");
            service.shutdown();
        }
    
        private static void print(String s) {
            LocalDateTime localDateTime = LocalDateTime.now();
            String format = localDateTime.format(formatter);
            System.out.println(s + format);
        }
    

    结果

    1:2019-10-15 11:53:54
    3:2019-10-15 11:53:54
    Thread name : pool-1-thread-1
    2:2019-10-15 11:54:00
    Thread name : pool-1-thread-2
    2:2019-10-15 11:54:00
    
    

    明白它的延迟原理和Timer一样,可以知道如果我把核心线程数改成1,则效果和Timer类似

    • 结果如下,两个任务延迟3秒,前一个任务会导致后一个任务延迟
    1:2019-10-15 11:57:40
    3:2019-10-15 11:57:40
    Thread name : pool-1-thread-1
    2:2019-10-15 11:57:46
    Thread name : pool-1-thread-1
    2:2019-10-15 11:57:49
    
    • 它的优势在于可以多线程执行,一定程度上避免任务间互相影响,同时单个任务异常不影响其它任务

    1.3. 时间轮(延迟消息)

    1

    • 本质是环形数组,比如上述8个节点的时间轮,每个节点存放任务队列,比如我们需要新建5s延迟的任务,就放5的节点,新建10s延迟的任务就放2的节点,设延迟时间为n,则节点位置为n%8,同时需记下轮询圈数n/8
    • 优势:当任务数量非常多的时候采用这样环形数组加队列是个效率比较高的选择
    • 想要了解更多时间轮实现,可以参考文章下的参考博客

    1.4. 分布式定时任务

    • github上也有些开源的分布式定时任务的方案,可以直接使用
    • xxl_jobelastic-job-litelight-task-scheduler,以哪个火用哪个的原则,那还是xxl_job的星星最多,另外两个已经两年没更新了

    参考博客:
    延迟消息之时间轮

  • 相关阅读:
    如何控制Yahoo! Slurp蜘蛛的抓取频度_国外博客资源站_百度空间
    PHP学习之十:foreach
    asp.net 上传控件
    程序员转型不得不说的事 成为管理者
    博客无法使用外站图片,暂停更新一段时间
    C#控制光驱开关
    通过输入方式在Android上进行微博OAuth登录
    .Net 绑定Dropdownlist的时自定义组合字段后显示
    XNA那些事(四) 3D知识初步
    jQuery实现图片延迟加载
  • 原文地址:https://www.cnblogs.com/sky-chen/p/11678081.html
Copyright © 2020-2023  润新知