Java 并发系列之七:java 阻塞队列(7个)
1. 基本概念
2. 实现原理
3. ArrayBlockingQueue
4. LinkedBlockingQueue
5. LinkedBlockingDeque
6. PriorityBlockingQueue
7. DelayQueue
8. SynchronousQueue
9. LinkedTransferQueue
10. 小总结
11. txt
1 Java阻塞队列(7个) 2 基本概念 3 阻塞队列是一个支持两个附加操作的队列 4 两个附加操作 5 支持阻塞的插入方法 6 当队列满时,队列会阻塞插入元素的线程,直到队列不满 7 支持阻塞的移除方法 8 当队列空时,队列会阻塞获取元素的线程,直到队列非空 9 阻塞队列的4中处理方式 10 抛出异常 11 定义 12 当队列满时,如果再往队列里插入元素,会抛出IllegalStateException 13 当队列空时,如果在从队列里移除元素,会抛出NoSuchElementException 14 具体方法 15 插入方法 16 add(e) 17 移除方法 18 remove() 19 检查方法 20 element() 21 返回特殊值 22 定义 23 往队列插入元素时,会返回元素是否插入成功,成功返回true 24 从队列移除元素时,如果没有则会返回null 25 具体方法 26 插入方法 27 offer(e) 28 移除方法 29 poll() 30 检查方法 31 peek() 32 一直阻塞 33 定义 34 当队列满时,如果生产者线程再往队列里put插入元素,队列会一直阻塞生产者线程,直到队列可用或者响应中断退出 35 当队列空时,如果消费者线程从队列里take移除元素,队列会阻塞住消费者线程,直到队列不为空 36 具体方法 37 插入方法 38 put(e) 39 移除方法 40 take() 41 检查方法 42 不可用 43 超时退出 44 定义 45 当队列满时,如果生产者线程再往队列里插入元素,队列会阻塞生产者线程一段时间,超过了指定的时间,生产者线程就会退出 46 具体方法 47 插入方法 48 offer(e, time, unit) 49 移除方法 50 poll( time, unit ) 51 检查方法 52 不可用 53 注意: 如果是无界阻塞队列, 队列不可能出现满的情况,使用put或offer方法永远不会被阻塞,而且使用offer方法时,永远返回true 54 应用场景 55 生产者消费者场景 56 阻塞队列就是生产者用来存放元素,消费者用来获取元素的容器 57 实现原理 58 使用通知模式 59 当生产者往满的队列里添加元素时会阻塞住生产者,当消费者消费了一个队列中的元素后,会通知生产者当前队列可用 60 小总结 61 一个抽象类 62 AbstractQueue,改类在Queue接口中扮演着非常重要的作用,该类提供了对queue操作的骨干实现 63 一个接口 64 BlockingQueue继承java.util.Queue为阻塞队列的核心接口,提供了在多线程环境下的出列、入列操作,作为使用者,则不需要关心队列在什么时候阻塞线程,什么时候唤醒线程,所有一切均由BlockingQueue来完成。 65 七个阻塞队列 66 ArrayBlockingQueue 67 一个由数组组成的有界阻塞队列 68 LinkedBlockingQueue 69 一个由链表组成的有界阻塞队列 70 LinkedBlockingDeque 71 一个由链表组成的双向阻塞队列 72 PriorityBlockingQueue 73 一个支持优先级排序的无界阻塞队列 74 DelayQueue 75 一个使用优先级队列实现的无界阻塞队列 76 SynchronousQueue 77 一个不存储元素的阻塞队列 78 LinkedTransferQueue 79 一个由链表组成的无界阻塞队列 80 即可以像其他的BlockingQueue一样有容量又可以像SynchronousQueue一样不会锁住整个队列 81 有界 82 对读或者写都是锁上整个队列,在并发量大的时候,各种锁是比较耗资源和耗时间的 83 LinkedTransferQueue 84 特性 85 一个由链表组成的无界阻塞队列,FIFO 86 即可以像其他的BlockingQueue一样有容量又可以像SynchronousQueue一样不会锁住整个队列 87 相对于其他的阻塞队列,LinkedTransferQueue多了tryTransfer和transfer方法 88 LinkedTransferQueue是一个聪明的队列。它是ConcurrentLinkedQueue(无界非阻塞队列)、SynchronousQueue (公平模式下)、无界的LinkedBlockingQueues等的超集。 89 LinkedTransferQueue采用一种预占模式。什么意思呢?有就直接拿走,没有就占着这个位置直到拿到或者超时或者中断。即消费者线程到队列中取元素时,如果发现队列为空,则会生成一个null节点,然后park住等待生产者。后面如果生产者线程入队时发现有一个null元素节点,这时生产者就不会入列了,直接将元素填充到该节点上,唤醒该节点的线程,被唤醒的消费者线程拿东西走人。 90 Node节点 91 isData:表示该节点是存放数据还是获取数据 92 item:存放数据,isData为false时,该节点为null,为true时,匹配后,该节点会置为null 93 next:指向下一个节点 94 waiter:park住消费者线程,线程就放在这里 95 重要操作 96 LinkedTransferQueue提供了add、put、offer三类方法,用于将元素插入队列中 97 LinkedTransferQueue提供了poll、take方法用于出列元素 98 transfer方法 99 如果当前有消费者正在等待接收元素,transfer可以把生产者传入的元素立刻传输给消费者 100 如果没有,会将元素存放在队列的tail节点,并等到该元素被消费者消费(CAS)了才返回 101 tryTransfer方法 102 试探生产者传入的元素是否能直接传给消费者 103 如果没有消费者等待接受元素,则返回false 104 两者的区别是tryTransfer方法无论消费者是否接收,方法立即返回 105 transfer方法必须等到消费者消费了才返回 106 SynchronousQueue 107 特性 108 一个不存储元素的阻塞队列 109 SynchronousQueue没有容量。与其他BlockingQueue不同,SynchronousQueue是一个不存储元素的BlockingQueue。每一个put操作必须要等待一个take操作,否则不能继续添加元素,反之亦然。 110 因为没有容量,所以对应 peek, contains, clear, isEmpty … 等方法其实是无效的。例如clear是不执行任何操作的,contains始终返回false,peek始终返回null。 111 SynchronousQueue分为公平和非公平,默认情况下采用非公平性访问策略,当然也可以通过构造函数来设置为公平性访问策略(为true即可,FIFO)。 112 SynchronizedQueue的吞吐量高于LinkedBlockingQueue和ArrayBlockingQueue 113 若使用 TransferQueue, 则队列中永远会存在一个 dummy node 114 应用场景 115 传递性场景 116 负责把生产者线程处理的数据直接传递给消费者线程 117 生产者的线程和消费者的线程同步以传递某些信息、事件或者任务 118 内部类 119 Transferer 120 Transferer为SynchronousQueue的内部类,它提供了一个方法transfer(),该方法定义了转移数据的规范 121 TransferQueue 122 SynchronousQueue采用队列TransferQueue来实现公平性策略 123 TransferStack 124 采用堆栈TransferStack来实现非公平性策略 125 TransferQueue、TransferStack继承Transferer 126 两种都是通过链表实现的,其节点分别为QNode,SNode 127 Exchanger 可能被视为 SynchronousQueue 的双向形式。 128 它提供一个同步点,用于进行线程间成对配对及交换数据, 129 DelayQueue 130 特性 131 一个使用优先级队列实现的无界阻塞队列,使用PriorityQueue来实现,延时获取 132 DelayQueue是一个支持延时获取元素的无界阻塞队列。里面的元素全部都是“可延期”的元素,列头的元素是最先“到期”的元素,如果队列里面没有元素到期,是不能从列头获取元素的,哪怕有元素也不行。也就是说只有在延迟期到时才能够从队列中取元素。 133 PriorityQueue作为一个容器,容器里面的元素都应该实现Delayed接口,在每次往优先级队列中添加元素时以元素的过期时间作为排序条件,最先过期的元素放在优先级最高。 134 与Exchanger有一拼 135 应用场景 136 缓存系统的设计 137 可是用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了 138 定时任务调度 139 使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行,比如TimerQueue就是使用DelayQueue实现的。 140 内部结构|重要参数 141 可重入锁ReentrantLock 142 用于阻塞和通知的Condition对象 143 根据Delay时间排序的优先级队列:PriorityQueue 144 用于优化阻塞通知的线程元素leader,通过leader来减少不必要阻塞。 145 实现 146 继承Delay接口 147 创建对象,初始化基础数据,time记录当前对象延迟到什么时候能用,sequenceNumber表示元素在队列中的先后顺序 148 实现getDelay方法,该方法返回当前元素还需要延时多长时间,单位是纳秒 149 实现compareTo方法来指定元素的顺序 150 实现延时阻塞队列 151 当消费者线程从队列里获取元素时,如果元素没有达到延时时间,就阻塞当前线程 152 重要操作 153 offer 154 offer(E e)就是往PriorityQueue中添加元素,同PriorityBlockingQueue,但是注意有一点如果当前元素的对首元素(优先级最高),leader设置为null,唤醒所有等待线程 155 take 156 首先是获取对首元素,如果对首元素的延时时间 delay <= 0 ,则可以出对了,直接return即可。否则设置first = null,这里设置为null的主要目的是为了避免内存泄漏。如果 leader != null 则表示当前有线程占用,则阻塞,否则设置leader为当前线程,然后调用awaitNanos()方法超时等待。 157 PriorityBlockingQueue 158 特性 159 一个支持优先级排序的无界阻塞队列,支持优先级 160 PriorityBlockingQueue底层采用二叉堆来实现的,添加操作是添加到最后不断“上冒”,删除操作是删掉根将最后一个提到根不断“下掉”。 161 内部仍然采用可重入锁ReentrantLock来实现同步机制,但是这里只有一个notEmpty的Condition,因为无界队列,插入总是会成功,除非资源耗尽 162 默认情况下元素采用自然顺序升序排序,当然我们也可以通过构造函数来指定Comparator来对元素进行排序。需要注意的是PriorityBlockingQueue不能保证同优先级元素的顺序。 163 分类 164 - 最大堆 165 - 父节点的键值总是大于或等于任何一个子节点的键值 166 - 最小堆 167 - 父节点的键值总是小于或等于任何一个子节点的键值 168 LinkedBlockingDeque 169 特性 170 一个由链表组成的双向阻塞队列,LinkedBlockingDeque支持FIFO、FILO两种操作方式。 171 LinkedBlockingDeque底层实现机制是通过互斥锁ReentrantLock 来实现,notEmpty 、notFull 两个Condition做协调生产者、消费者问题。 172 LinkedBlockingDeque是可选容量的,在初始化时可以设置容量防止其过度膨胀,如果不设置,默认容量大小为Integer.MAX_VALUE。 173 多了一个操作队列的入口,在多线程同时入队时,减少了一般的竞争 174 内部类Node 175 E item; 176 Node<E> prev; 177 Node<E> next; 178 重要操作 179 入队 180 putFirst(E e) :将指定的元素插入此双端队列的开头,必要时将一直等待可用空间。 181 putLast(E e) :将指定的元素插入此双端队列的末尾,必要时将一直等待可用空间。 182 出队 183 pollFirst():获取并移除此双端队列的第一个元素;如果此双端队列为空,则返回 null。 184 pollLast():获取并移除此双端队列的最后一个元素;如果此双端队列为空,则返回 null。 185 其他 186 LinkedBlockingDeque大部分方法都是通过linkFirst、linkLast、unlinkFirst、unlinkLast这四个方法来实现的,因为是双向队列,所以他们都是针对first、last的操作 187 LinkedBlockingDeque 的add、put、offer、take、peek、poll系列方法都是通过调用XXXFirst,XXXLast方法。 188 应用 189 工作窃取模式中 190 LinkedBlockingQueue 191 特性 192 一个由链表组成的有界阻塞队列,该队列采用FIFO的原则对元素进行排序添加的。 193 LinkedBlockingQueue是通过互斥锁ReentrantLock 来实现,notEmpty 、notFull 两个Condition做协调生产者、消费者问题。 194 默认和最大长度是Integer.MAX_VALUE 195 ArrayBlockingQueue 196 特性 197 ArrayBlockingQueue,一个由数组实现的有界阻塞队列。该队列采用FIFO的原则对元素进行排序添加的。 198 ArrayBlockingQueue内部使用可重入锁ReentrantLock + Condition来完成多线程环境的并发操作。 199 ArrayBlockingQueue为有界且固定,其大小在构造时由构造函数来决定,确认之后就不能再改变了。 200 ArrayBlockingQueue支持对等待的生产者线程和使用者线程进行排序的可选公平策略,但是在默认情况下不保证线程公平的访问,在构造时可以选择公平策略(fair = true)。公平性通常会降低吞吐量,但是减少了可变性和避免了“不平衡性”。 201 重要参数 202 items,一个定长数组,维护ArrayBlockingQueue的元素 203 takeIndex,int,为ArrayBlockingQueue队首位置 204 putIndex,int,ArrayBlockingQueue队尾位置 205 count,元素个数 206 lock,锁,ArrayBlockingQueue出列入列都必须获取该锁,两个步骤公用一个锁 207 notEmpty,出列条件:不空 208 notFull,入列条件:不满 209 重要操作 210 入队 211 add(E e) :将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则抛出 IllegalStateException 212 offer(E e) :将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则返回 false 213 offer(E e, long timeout, TimeUnit unit) :将指定的元素插入此队列的尾部,如果该队列已满,则在到达指定的等待时间之前等待可用的空间 214 put(E e) :将指定的元素插入此队列的尾部,如果该队列已满,则等待可用的空间 215 出队 216 poll() :获取并移除此队列的头,如果此队列为空,则返回 null 217 poll(long timeout, TimeUnit unit) :获取并移除此队列的头部,在指定的等待时间前等待可用的元素(如果有必要) 218 remove(Object o) :从此队列中移除指定元素的单个实例(如果存在) 219 take() :获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)