队列的实现
来源:互联网 发布:云计算和传统的区别 编辑:程序博客网 时间:2024/06/07 23:59
转自--》http://blog.yanzhenjie.com 先Mark,后学习
队列的基本理解
用生活中的一个情景来举个栗子,前段时间很火爆的电视剧《人民的名义》中有一个丁义珍式的窗口大家应该都知道了,我们不说《人民的名义》也不说丁义珍,我们来说说这个办事窗口。
我们知道在某机构上班期间,窗口一直是开着的,有人去办事了窗口就开始做事,没人办事了窗口就处于等待的状态,如果去办事的人特别多,这些办事的人就必须排队(我特别讨厌在要排队的地方不排队的人,呼吁大家如果不是性命攸关的急事,一定要排队,谁不想早点办完事呢),窗口也可能是多个,而这些窗口中也可能有一些特别窗口,比如军人优先办理。
在说队列之前说两个名词:
Task
是任务,TaskExecutor
是任务执行器。
而我们今天要说的队列就完全符合某机构这个情况,队列在有Task
进来的时候TaskExecutor
就立刻开始执行Task
,当没有Task
的时候TaskExecutor
就处于一个阻塞状态,当有很多Task的时候Task也需要排队,TaskExecutor
也可以是多个,并且可以指定某几个Task
优先执行或者滞后执行。
综上所说我们得出一个这样的关系:队列相当于某机构,TaskExecutor相当于窗口,办事者
就是Task
。
本文源码下载地址:
http://download.csdn.net/detail/yanzhenjie1003/9841188
项目源码使用IDEA写的,你可以直接import到你的IDEA,或者把源码直接拷贝到Eclipse或者AndroidStudio。
普通队列
当然很多机构也没有设置什么军人优先的窗口,所以队列也有不带优先级的队列,因此我们先来实现一个非优先级的队列。
我们常用的队列接口有:
Queue<E>
、BlockingQueue<E>
,基于上面我们说的特点,我们用BlockingQueue<E>
来实现任务队列,BlockingQueue<E>
的实现有很多,这里我们选择LinkedBlockingQueue<E>
。
和上述某机构不一样,某机构可以先有机构,再有窗口,再有办事者。但是我们写代码的时候,要想写一个队列,那么务必要在队列中写TaskExecutor
,那么就得先写好TaskExecutor
类,以此类推就得先有Task
类。
因此我们先写一个Task
的接口,也就是办事的人,我把它设计为接口,方便办各种不同事的人进来:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
接下来再写一个TaskExecutor
的类,也就是窗口,用来执行Task
,认真看注释,非常有助于理解:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
这里要稍微解释下BlockingQueue<T>#take()
方法,这个方法当队列里面的item
为空的时候,它会一直处于阻塞状态,当队列中进入item
的时候它会立刻有一个返回值,它就和ServerSocket.accept()
方法一样,所以我们把它放入一个Thread
中,以免阻塞调用它的线程(android中可能是主线程)。
办事的人和窗口都有了,下面我们封装一个队列,也就是某机构,用来管理这些窗口:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
某机构、窗口、办事的人都有了,下面我们就派一个人去一件具体的事,但是上面我的Task
是一个接口,所以我们需要用一个类来实现这个接口,来做某一件事:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
下面就让我们模拟的虚拟世界运行一次:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
没错,队列按照我们理想的状况打印出来了:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
上面我门只开了一个窗口,下面我多开几个窗口:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
这里要说明一下,在初始化的时候我们开了3个窗口,内部的顺序应该是这样的:
当某机构的大门开了以后,第一个办事的人进去到了第一个窗口,第二个办事的人进去到了第二个窗口,第三个办事的人进去到了第三个窗口,第四个办事的人进去排队在第一位,当第一、第二、第三个窗口中不论哪一个窗口的事办完了,第四个人就去哪一个窗口继续办事,第五个人等待,一次类推。这样子就达到了队列同事并发三个任务的效果。
这就是一个普通的队列,其它的一些特性也是基于此再次封装的,那么下面我就基于此再把人物的优先级加上,也就是我们上面说的特殊窗口->军人优先!
优先级队列
我们排队等待办事的时候,来了一个办事的人,那么如何判断这个办事人是否可以优先办理呢?那就要判断它是否具有优先的权限甚至他可以优先到什么程度。
所以我们需要让这个Task
有一标志,那就是优先级,所以我用一个枚举类标记优先级:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
这里我把分了四个等级:最低
、默认
、高
、立刻
,这个等级肯定要给到我们的办事的人,也就是Task
:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
可以设置优先级和可以拿到优先级。
下面我们要把上面的LinkedBlockingQueue
替换成PriorityBlockingQueue<E>
,因为它可以自动做到优先级的比较,它要求泛型<E>
,也就是我们的Task
必须实现Comparable<E>
接口,而Comparable<E>
有一个compareTo(E)
方法可以对两个<T>
做比较,因此我们的队列需要改一下实现的方法:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
然后ITask
接口继承Comparable<E>
接口:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
因为有setPriority(Priority)
方法和getPriority()
方法和Comparable<E>
的compareTo(E)
方法,所以我们的每一个Task
都需要实现这几个方法,这样就会很麻烦,所以我们封装一个BasicTask
:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
其它都好说,我们看到compareTo(E)
方法就不太理解了,这里说一下这个方法:
compareTo(E)
中传进来的E是另一个Task
,如果当前Task
比另一个Task
更靠前就返回负数,如果比另一个Task
靠后,那就返回正数,如果优先级相等,那就返回0。
这里要特别注意,我们看到上面当两个Task
优先级不一样的时候调用了Priority.orinal()
方法,并有后面的orinal
减去了当前的orinal
,怎么理解呢?首先要理解Priority.orinal()
方法,在Java中每一个枚举值都有这个方法,这个枚举的值是它的下标+1,也就是[index + 1]
,所以我们写的Priority
类其实可以这样理解:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
继续,如果给当前Task
比较低,给compareTo(E)
中的Task
设置的优先级别比较高,那么Priority
不一样,那么返回的值就是整数,因此当前Task
就会被PriorityBlockingQueue<E>
排到后面,如果调换那么返回结果也就调换了。
但是我们注意到me == it ? [...] : it.ordinal() - me.ordinal();
中的[...]
是什么鬼啊?因为这里缺一段代码呀哈哈哈(这个作者怎么傻乎乎的),这一段代码的意思是当优先级别一样的时候怎么办,那就是谁先加入队列谁排到前面呗,那么怎样返回值呢,我们怎么知道哪个Task
先加入队列呢?这个时候可爱的我就出现了,我给它给一个序列标记它什么时候加入队列的不久完事了,于是我们可以修改下ITask
接口,增加两个方法:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
我们用setSequence(int)
标记它加入队列的顺序,然后因为setSequence(int)
和getSequence()
是所有Task
都需要实现的,所以我们在BasicTask
中实现这两个方法:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
看到了吧,刚才的[...]
已经变成了this.getSequence() - another.getSequence()
,这里需要和上面的it.ordinal() - me.ordinal();
的逻辑对应,上面说到如果给当前Task
比较低,给compareTo(E)
中的Task
设置的优先级别比较高,那么Priority
不一样,那么返回的值就是整数,因此当前Task
就会被PriorityBlockingQueue<E>
排到后面,如果调换那么返回结果也就调换了。
这里的逻辑和上面对应就是和上面的逻辑相反,因为这里是当两个优先级一样时的返回,上面是两个优先级不一样时的返回,所以当优先级别一样时,返回负数表示当前Task
在前,返回正数表示当前Task
在后,正好上面上的逻辑对应。
接下来就是给Task
设置序列了,于是我们在TaskQueue
中的T void add(T)
方法做个手脚:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
这里我们使用了AtomicInteger
类,它的incrementAndGet()
方法会每次递增1,其实它相当于:
- 1
- 1
其它具体用法请自行搜索,这里不再赘述。
到此为止,我们的优先级别的队列就实现完毕了,我们来做下测试:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
没错这就是我们看到的效果:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
到这里就结束啦,本文源码下载地址:
http://download.csdn.net/detail/yanzhenjie1003/9841188
- 队列的实现:顺序队列
- 队列的实现:链式队列
- 队列----循环队列的实现
- 队列实现 队列的链式结构实现
- 堆栈,队列的实现
- 链队列的实现
- 队列的实现
- 队列的实现
- java 实现的队列
- 工作队列的实现
- 工作队列的实现
- 用链表实现的队列
- 队列的实现
- 队列的实现
- 队列的实现
- 队列的数组实现
- 循环队列的实现
- 数据结构 队列的实现
- ListView滚动监听
- 系统学习深度学习(三十)--BiLSTM
- make 2>&1 | tee log.txt 命令解析
- HttpServletRequest
- 获取所有库的大小
- 队列的实现
- if else与switch区别
- dd大牛的《背包九讲》
- 2017百度之星Astar资格赛 1001度度熊保护村庄
- ARM指令adr adrl ldr mov简单科普
- 关于数据类型的判断不使用typeof的精确定位
- 如何使用ArcMap将.mxd文件发布成.sd文件
- 二进制中1的个数
- maven-dependency-plugin (goals "copy-dependencies","unpack") is not supported by m2e