数组阻塞队列ArrayBlockingQueue:
- 特点:先进先出(FIFO)、读写互斥
- 数据结构:静态数组,容量固定必须指定长度,没有扩容机制,没有元素的位置使用null占位(为null的位置也是占用着数据空间)
- 锁:ReentrantLock存取是同一把锁,操作的是同一个对象数组
- 阻塞:
- Condition notEmpty;当出队时(取出数据),当队列count(队列的元素个数)为0,没有数据可取,这时会阻塞该对象上
- Condition notFull;当入队时(放入数据),当队列count(队列的元素个数)和数组的length相等时,放不进元素,这时会阻塞在该对象上
- 入队:从队首开始添加元素,记录putIndex(到队尾时置为0),每次入队一个数据时,不管是否阻塞,都会唤醒一次Condition notEmpty
- 出队:从队首开始取元素,记录takeIndex,唤醒Condition notFull
链表阻塞队列LinkedBlockingQueue:
- 特点:先进先出(FIFO)、读写互斥
- 数据结构:链表Node,可以指定容量,不指定时默认为Integer.MAX_VALUE,内部类Node存储元素
- 锁分离:存取互不排斥,操作的是不同的Node对象,取:ReentrantLock takeLock,存:ReentrantLock putLock
- 阻塞:和ArrayBlockingQueue一致
- 入队:队尾入队,记录last节点
- 出队:队首出队,记录head几点
- 删除元素时,存取都会加锁
同步队列SynchronousQueue:
- 两种模式(通过一个boolean属性指定):
- 公平模式(链表):TransferQueue,节点使用QNode,队尾匹配,队头出队,先进先出(FIFO)
- 非公平模式(栈):TransferStack,节点使用SNode,栈顶匹配,出入口都在栈顶,后进先出(LIFO)
- 存取数据:
- 存取都是调用Transferer.transfer()函数,区分存取是根据是否携带了数据e
- put、offer为生产者,携带了数据e,为Data模式,设置到QNode/SNode属性中
- take、poll为消费者,不携带数据e,为REQUEST模式,设置到QNode/SNode属性中
- 没存或取到数据会阻塞
- 数据结构:链表Node
- 锁:CAS + 自旋
- 阻塞:LockSupport
LinkedTransferQueue:
- 数据结构:链表Node
- 锁:CAS + 自旋
- 阻塞:LockSupport
- 放入元素:往队列中放元素可以自己控制是否需要阻塞队列
- 取元素:没取到数据时会阻塞
- 特点:与SynchronousQueue基本一样
优先级的阻塞队列PriorityBlockingQueue:
- 数据结构:数组 + 平衡二叉树堆,可以指定初始容量,会自动扩容,最大容量为Integer.MAX_VALUE
- 锁:ReentrantLock存取是同一把锁
- 阻塞:Condition notEmpty,出队,队列为空时阻塞
- 入队:
- 不阻塞,永远返回成功,无界(逻辑上是无界,有可能由于资源耗尽导致OutOfMemoryError)
- 根据比较器进行堆化:根据二叉堆进行排序,自下而上
- 出队:
- 弹出堆顶元素
- 堆尾元素放到堆顶
- 自上而下堆化,堆化:堆上的元素根据较器对元素进行排序
- 为空则阻塞
- 按照优先级排序,创建PriorityBlockingQueue队列时可以传入一个比较器Comparator,优先级最高的会先出队。
链表阻塞双端队列LinkedBlockingDeque:
- 数据结构:链表Node,可以指定容量,不指定时默认为Integer.MAX_VALUE,内部类Node存储元素
- 锁:ReentrantLock存取是同一把锁
- 阻塞:
- Condition notEmpty;当出队时(取出数据),当队列count(队列的元素个数)为0,没有数据可取,这时会阻塞该对象上
- Condition notFull;当入队时(放入数据),当队列count(队列的元素个数)和数组的length相等时,放不进元素,这时会阻塞在该对象上
- 入队:队首队尾都可以
- 出队:队首队尾都可以
延迟队列DelayQueue:
- 数据结构:PriorityQueue优先级队列,与PriorityBlockingQueue类似,但是没有阻塞功能
- 锁:ReentrantLock
- 阻塞:Condition available
- 队列不允许有空元素,而且放入的元素必须实现Delayed接口
- 入队:不阻塞,无界队列,与优先级队列入队相同,唤醒出队的Condition available
- 出队:
- 为空则阻塞available
- 检查堆顶元素过期时间
- 小于等于0,则取出
- 大于0,则阻塞,然后判断leader线程是否为空
- 不为空(已经有线程正在阻塞),考虑到优先级的问题,所以直接阻塞
- 为空,则将当前线程置为leader,并按照过期时间进行阻塞,到期后会自动唤醒