ConcurrentLinkedQueue

本文深入剖析了Java中ConcurrentLinkedQueue的内部实现原理及关键方法。ConcurrentLinkedQueue是JDK提供的高并发队列,使用链表作为数据结构,支持高效并发读写。文章详细介绍了ConcurrentLinkedQueue的节点结构、核心方法如peek、poll、offer等,并讨论了其在多线程环境下的表现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

队列、链表之类的数据结构及其常用。Java中,ArrayList和Vector都是使用数组作为其内部实现。两者最大的不同在于:Vector是线程安全的,而ArrayList不是。此外LinkedList使用链表的数据结构实现了List,但并不是线程安全的,就像之前包装HashMap,这里我们可以使用Collections.synchronizedList()来包装任意List。此时,生成的List对象就是线程安全的。

public static List<String> l = Collections.synchronizedList(new LinkedList<String>());

高效读写的队列:深度剖析ConcurrentLinkedQueue

 JDK中,提供了一个ConcurrentLinkedQueue类来实现高并发的队列。这个队列使用链表作为其数据结构。这个类算是高并发环境中性能最好的队列。高性能是因为其内部复杂的实现。

链表内的节点:

    private static class Node<E> {
        volatile E item;
        volatile Node<E> next;
    ...

其实这是一个静态内部类,item是来表示元素的。next表示当前Node的下一个元素。 
对Node进行操作时,使用了CAS操作:

//表示设置当前Node的item值。第一个参数为期望值,第二个参数为设置目标值。当当前值等于期望值时(就是没有被其他人改过),就会将目标设置为val。
boolean casItem(E cmp, E val) {
    return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
}

//和上个方法类似,是用来设置next字段。
boolean casNext(Node<E> cmp, Node<E> val) {
    return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}

ConcurrentLinkedQueue内部有两个重要的字段:head和tail,分别表示链表的头部和尾部,当然都得是Node类型。对于head来说,它永远不会为null,并且通过head和succ()后继方法一定能完整地遍历整个链表。对于tail来说,它自然表示队列的末尾。 
以下是构造函数:

    public ConcurrentLinkedQueue() {
        head = tail = new Node<E>(null);
    }

但是ConcurrentLinkedQueue的内部实现非常复杂,它允许在运行时链表处于多个不同的状态。以tail为例,一般来说我们期望tail总是作为链表的末尾,但实际上tail的更新并不是及时的,可能会产生拖延现象。就是说,有可能指向的并不是真正的尾巴,真正的可能在后面一个或者多个。

有三个函数需要注意的:

peek()获取元素 不移除头结点

poll() 获取元素并且在队列中移除,如果队列为空返回null

offer()将指定元素插入到此队列的尾部。

toArray

返回以恰当顺序包含此队列所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。如果指定的数组能容纳队列,则将该队列返回此处。否则,将分配一个具有指定数组的运行时类型和此队列大小的新数组。
如果指定的数组能容纳队列,并有剩余的空间(即数组的元素比队列多),那么会将紧接队列尾部的元素设置为 null。
像 toArray() 方法一样,此方法充当基于数组的 API 与基于 collection 的 API 之间的桥梁。更进一步说,此方法允许对输出数组的运行时类型进行精确控制,在某些情况下,这可以用来节省分配开销。
    假定 x 是只包含字符串的一个已知队列。以下代码用来将该队列转储到一个新分配的 String 数组.


注意:

1.ConcurrentLinkedQueue的API原来.size()是要遍历一遍集合的,所以尽量要避免用size而改用isEmpty().

2.如果直接使用它提供的函数,比如:queue.add(obj); 或者 queue.poll(obj);,这样我们自己不需要做任何同步。
但如果是非原子操作,比如:

[java]  view plain  copy
 print ?
  1. if(!queue.isEmpty()) {    
  2.    queue.poll(obj);    
  3. }    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值