zoukankan      html  css  js  c++  java
  • 定时任务 & 定时线程池 ScheduledThreadPoolExecutor

    ScheduledThreadPoolExecutor 

    提交的任务按照执行的时间排序放入到 DelayQueue 队列中。

    • DelayQueue内部封装了一个PriorityQueue,它会根据time的先后时间排序(time小的排在前面),若time相同则根据sequenceNumber排序( sequenceNumber小的排在前面);
    • DelayQueue也是一个无界队列;  

    ScheduledThreadPoolExecutor 定时线程池类的类结构图 

     SchedualedThreadPoolExecutor 接收SchduledFutureTask类型的任务,是线程池调度任务的最小单位,有三种提交任务的方式:

    •  schedule:延迟多长时间之后只执行一次;
    •  scheduledAtFixedRate:延迟指定时间后执行一次,之后按照固定的时长周期执行;
    •  scheduledWithFixedDelay:延迟指定时间后执行一次,之后按照:上一次任务执行时长 + 周期的时长 的时间去周期执行;
    public static void main(String[] args) {
            ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
    
            pool.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println("延迟执行");
                }
            },1, TimeUnit.SECONDS);
    
            /**
             * 这个执行周期是固定,不管任务执行多长时间,还是每过3秒中就会产生一个新的任务
             */
            pool.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    //这个业务逻辑需要很长的时间,定时任务去统计一张数据上亿的表,财务财务信息,需要30min
                    System.out.println("重复执行1");
                }
            },1,3,TimeUnit.SECONDS);
    
            /**
             * 假设12点整执行第一次任务12:00,执行一次任务需要30min,下一次任务 12:30 + 3s 开始执行
             */
            pool.scheduleWithFixedDelay(new Runnable() {
                @Override
                public void run() {
                    //30min
                    try {
                        Thread.sleep(60000 * 30);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("" + new Date() +"重复执行2");
                }
            },1, 3, TimeUnit.SECONDS);
     }
    

    任务提交到线程池中,任务排序的 siftUp 方法

    private void siftUp(int k, RunnableScheduledFuture<?> key) {
    	// 找到父节点的索引
    	while (k > 0) {
    		// 获取父节点
    		int parent = (k ­- 1) >>> 1;
    		RunnableScheduledFuture<?> e = queue[parent];
    		
    		// 如果key节点的执行时间大于父节点的执行时间,不需要再排序了
    		if (key.compareTo(e) >= 0)
    			break;
    			
    		// 如果key.compareTo(e) < 0,说明key节点的执行时间小于父节点的执行时间,需要把父节点移到后面
    		queue[k] = e;
    		setIndex(e, k);
    		
    		// 设置索引为k
    		k = parent;
    	}
    	
    	// key设置为排序后的位置中
    	queue[k] = key;
    	setIndex(key, k);
    }
    

      循环的根据key节点与它的父节点来判断,如果key节点的执行时间小于父节点,则将两个节点交换,使执行时间靠前的节点排列在队列的前面。

      可以理解为一个树形的结构,最小点堆的结构;父节点一定小于子节点

     

     实际上所谓的排序并不是绝对的按照顺序大小去排的,只保证了队列最前端的最小。为什么要这样设计呢?

    因为当队列中的数据过大的时候,要保证绝对的排序消耗是比较大的,而且我们没有必要去保证绝对排序,因为只需要保证队列头的数是最小的就可以了。

    30min
  • 相关阅读:
    python 基础到字符串学习
    Newtonsoft.Json 获取匿名类数据
    Abp Wcf结合使用问题
    Ef Migration 操作出现SQLEXPRESS
    No context type was found in the assembly 'xxx.xxxx'. CodeFirst Ef错误
    Ef Code First 发生”provider: SQL Network Interfaces, error: 26
    ef 多条数据插入处理方案(据说还有更好的)
    记录一次 HttpWebRequest 尝试自动重定向太多 错误
    NetCore 下使用RSA加密,解密;并且前端使用jsencrypt.js实现Rsa相关方法。
    The specified framework version '2.0' could not be parsed 错误处理
  • 原文地址:https://www.cnblogs.com/yufeng218/p/13211010.html
Copyright © 2011-2022 走看看