前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从Timer中学习优先队列的实现

从Timer中学习优先队列的实现

作者头像
用户3579639
发布2018-10-22 14:58:59
6920
发布2018-10-22 14:58:59
举报

从Timer中学习优先队列的实现

Timer是Java定时器的实现,用来调度定时执行的任务和执行一次的任务,就像JavaScript的setIntervalsetTimeout的意思,它也可以作为后台程序(Daemon)运行。

Timer

Timer调度的实现是通过TimerThread辅助类来实现的,在构造Timer实例的时候TimerThread就开始运行了;TimerThread需要从队列(TaskQueue)中获得需要被执行的任务(TimerTask),这是一个优先队列,TimeTask的executionTime(TimerTask设置要执行的时间Date.getTime()形式表示的)越小的越优先执行。

TimerThread如何调度

TaskQueue data structure

TaskQueue实现了优先队列的数据结构,内部是一个数组,数组内容实际上是从下标1开始填充的;它其实是用balanced binary heap来表示的,设父节点是queue[n],则它的两个字节点分别是queue[2*n]queue[2*n+1] 这个数据结构的API方法包括:

  • add(T object)
  • getMin()
  • removeMin()
  • fixDown(int k)
  • fixUp(int k)
  • heapify()

最重要的两个是fixDownfixUp,表示从queue[k]节点位置开始demotingpromoting

TaskQueue.fixDown

假设要操作的节点是queue[k],那么它的子节点分别是queue[j]queue[j+1]j=k*2,对queue[k]demoting处理,

代码语言:javascript
复制
    private void fixDown(int k) {
        int j;
        while ((j = k << 1) <= size && j > 0) {
            if (j < size &&
                queue[j].nextExecutionTime > queue[j+1].nextExecutionTime)
                j++; // j indexes smallest kid
            if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime)
                break;
            TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
            k = j;
        }
    }

1.首先比较两个子节点选出executionTime更小的那个, 2.如果右子节点的executionTime更小,则j++自增,这样就相当于选择了右节点(下次fixDown的位置从这里开始) 3.然后父节点和选中的更小executionTime子节点比较 4.如果父节点的executionTime更小,则交换父节点和这个子节点 那么为什么要执行2呢 之前,

图片描述
图片描述

之后,

图片描述
图片描述

如果是queue[j]变成了父节点就会破坏queue[n]<=queue[2*n+1]的关系。 然后就是一直fixDown到最后一个节点queue[size]。 我们可以思考下这个方法的时间复杂度是不是O(logn)

TaskQueue.fixUp

假设要操作的节点是queue[k],那么它的父节点分别是queue[j]j=k/2,对queue[k]promoting处理,

代码语言:javascript
复制
    private void fixUp(int k) {
        while (k > 1) {
            int j = k >> 1;
            if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
                break;
            TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
            k = j;
        }
    }

promotingdemoting简单一点,只需要比较queue[k]queue[j]两个节点,然后做交换即可。

TaskQueue other methods

通过fixUpfixDown的方法可以保证整个优先队列的关系,保证queue[1]的executionTime是最小的。 1.heapify()作用是构建堆,可以从{0,q[1],q[2],...,q[size]}的数组构建堆,来表示优先队列。

代码语言:javascript
复制
    void heapify() {
        for (int i = size/2; i >= 1; i--)
            fixDown(i);
    }

从中间下标到1做fixDown。

2.add(T object),往数组中添加元素,重新构建堆

代码语言:javascript
复制
    void add(TimerTask task) {
        // Grow backing store if necessary
        if (size + 1 == queue.length)
            queue = Arrays.copyOf(queue, 2*queue.length);

        queue[++size] = task;
        fixUp(size);
    }

不过需要判断数组空间是否有剩余,没有则扩展数组,并拷贝原来的数组元素,最后对queue[size]节点,也是新元素做fixUp。 3.getMin() 直接获得queue[1]元素 4.removeMin() 将queue[size]节点替换queue[1],然后对queue[1]做fixDown。

总结

这个TaskQueue的优先队列的实现代码是比较清晰的,重要方法的时间复杂度也可以算优秀。 阅读完这部分代码后或许可以进一步阅读PriorityQueue。 附:测试代码

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从Timer中学习优先队列的实现
    • Timer
      • TimerThread如何调度
        • TaskQueue data structure
          • TaskQueue.fixDown
          • TaskQueue.fixUp
          • TaskQueue other methods
        • 总结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档