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
  • 相关阅读:
    极简风格的LOGO,收集一波!
    如何利用AI软件中的混合器工具制作文字
    国内有哪些非常有设计感的 App?
    如何用PS制作花型背景图
    怎样去调整摄影作品的背景颜色
    如何用PS把数码拍摄的荷花照片制作成中国风的效果
    PS 抠人像如何抠得干净?
    如何用 Ps 制作毛玻璃穿透效果?
    化装品经过这样PS包装,身价立马翻10倍
    有哪些漂亮的中国风 LOGO 设计?
  • 原文地址:https://www.cnblogs.com/yufeng218/p/13211010.html
Copyright © 2011-2022 走看看