在日常中, 我们用到的数据结构有很多: 数组, 链表, 树等, 而在这些结构中, 还有一个叫做队列的存在。
和其他的集合相同, Java 原生提供了不同的实现。
而如果我们需要一个线程安全的队列的话, 可以基于实际的场景进行选择, 比如基于数组实现同时操作上会阻塞的 ArrayBlockingQueue, 基于链表同时也会阻塞的 LinkedBlockingDeque。
而今天我们聊的同样也是基于链表实现的线程安全的 ConcurrentLinkedQueue。
1 ConcurrentLinkedQueue 简单介绍
ConcurrentLinkedQueue 是 Java 中的一个并发队列实现, 位于 java.util.concurrent 包下。
它提供了一个基于链表实现的无界线程安全队列, 采用非阻塞算法实现并发操作。主要特点包括:
- 无界队列: ConcurrentLinkedQueue 不限制队列的大小, 可以动态地增长
- 非阻塞算法: 内部实现采用了非阻塞算法, 避免了传统锁的性能开销, 提高了并发性能
- 线程安全: 所有的操作都是线程安全的, 不需要额外的同步手段, 主用是通过 CAS(Compare and Swap)等无锁算法来实现的
- 高性能: 适用于高并发场景, 由于采用非阻塞算法, 避免了大部分锁的争用, 因而具有较好的性能表现
- FIFO 顺序: 队列遵循先进先出(FIFO)的原则, 保持元素插入的顺序
使用 ConcurrentLinkedQueue 可以方便地实现生产者 - 消费者模型, 适用于多线程环境下需要高效并发操作的场景。
需要注意的是, 由于该队列是无界的, 需要合理控制生产者的速度, 以防止队列无限制地增长。
2 存储数据的节点 Node
ConcurrentLinkedQueue 虽然叫做队列, 但是在内部的实现中是通过链表的方式实现的。
而说到链表就绕不开一个重要的属性: 链表的节点。
2.1 节点的属性定义
在 ConcurrentLinkedQueue 中的节点定义很简单
public class ConcurrentLinkedQueue<E> {
// 链表节点定义
private static class Node<E> {
/**
* 节点的数据
*/
volatile E item;
/**
* 下一个节点
*/
volatile Node<E> next;
}
}
这个节点的定义很简单, 就 2 个属性
- item: 存储在节点里面的数据
- next: 下一个节点
有些特殊的就是 item 和 next 都是用 volatile 修饰的, 保证了其可见性。
2.2 节点的方法
public class ConcurrentLinkedQueue<E> {
// 链表节点定义
private static class Node<E> {
/**
* CAS 更改 Node 中的数据域 item
* @param cmp 旧值
* @param val 新值
*/
boolean casItem(E cmp, E val) {
return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
}
/**
* CAS 更改 Node 中的指针域 next, 也就是修改当前节点的下一个节点
* @param val 新的值
*/
void lazySetNext(Node<E> val) {
UNSAFE.putOrderedObject(this, nextOffset, val);
}
/**
* 通过 CAS 将 Node 中的 next 指针从 cmp 设置为 val
* 涉及 cas 的旧值比较, 一样才会替换
* @param cmp 旧值
* @param val 新值
*/
boolean casNext(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
}
}
Node 本身就只提供了这 3 个方法, 这些方法实际上是通同 UNSAFE 的方法, 达到 CAS 式的修改值, 具备着原子性。
2.3 节点在 ConcurrentLinkedQueue 中的使用
public class ConcurrentLinkedQueue<E> {
/** 头结点 */
private transient volatile Node<E> head;
/** 尾结点 */
private transient volatile Node<E> tail;
public ConcurrentLinkedQueue() {
head = tail = new Node<E>(null);
}
}
head, tail 2 个节点分别代表了链表的头和尾, 而 ConcurrentLinkedQueue 就是通过这 2 个节点对队列进行管理的。
通过无参的构造函数构造后, ConcurrentLinkedQueue 的结构如下:

本文主要介绍Java中的并发队列ConcurrentLinkedQueue,它是基于链表实现的无界线程安全队列,采用非阻塞算法。文中详细分析了其存储节点、构造函数、新增和删除数据的方法,还探讨了多线程环境下的操作以及HOPS延迟更新设计,以提升性能。
最低0.47元/天 解锁文章
381

被折叠的 条评论
为什么被折叠?



