ConcurrentLinkedQueue (一)
ConcurrentLinkedQueue
主要讲一下在JDK8中,ConcurrentLikedQueue是如何入队,出队的。
首先我们要明白,ConcurrentLikedQueue是一种安全的没有边界的基于链表的队列,有头节点head,尾结点tail。
类似于
,上图是创建一个空的队列,只有head和tail节点,以下为源码:
public ConcurrentLinkedQueue() {
head = tail = new Node<E>(null);
}
对于入队来说,我们要了解的是tail节点不一定是最后一个节点,这是非常重要的。一般来说,当tail节点的next不为空时,在队尾加入新节点,更新tail指向尾节点;当tail的next节点为空时,在队尾加入新节点,不更新tail的位置。
源码中有一个注释:Both head and tail are permitted to lag. In fact, failing to update them every time one could is a significant optimization (fewer CASes). As with LinkedTransferQueue (see the internal documentation for that class), we use a slack threshold of two; that is, we update head/tail when the current pointer appears to be two or more steps away from the first/last node.
大概的意思是说不用每次都更新头尾节点,这是一个非常重要的优化。 使用的松弛阈值为2; 也就是说,当当前指针距离第一个/最后一个节点有两个或更多节点的距离时,我们更新head/tail。
在入队的时候,我们就能够很容易的看到上述所描述tail的特点。
入队:
/**
* Inserts the specified element at the tail of this queue.
* As the queue is unbounded, this method will never return {@code false}.
* 将指定的元素插入到此队列的末尾。因为队列是无界的,所以这个方法永远不会返回{@code false}。 * @return {@code true} (as specified by {@link Queue#offer})
* @throws NullPointerException if the specified element is null
*/
public boolean offer(E e) {
checkNotNull(e);
final Node<E> newNode = new Node<E>(e); //入队前构建节点 //从尾结点开始入队
for (Node<E> t = tail, p = t;;) {
Node<E> q = p.next;
if (q == null) { //tail是尾结点
// p is last node
//如果p是尾结点,设置p节点的next为newNode
if (p.casNext(null, newNode)) { //如果新节点添加入尾节点后面
// Successful CAS is the linearization point
// for e to become an element of this queue,
// and for newNode to become "live".
//成功的CAS是使e成为这个队列的一个元素,使newNode成为“活的”的线性化点。
if (p != t) // hop two nodes at a time一次跳转两个节点
casTail(t, newNode); // Failure is OK.
return true;
}
// Lost CAS race to another thread; re-read next
//丢失的CAS争用到另一个线程;重读next
}
else if (p == q)
// We have fallen off list. If tail is unchanged, it
// will also be off-list, in which case we need to
// jump to head, from which all live nodes are always
// reachable. Else the new tail is a better bet. p = (t != (t = tail)) ? t : head;
else
// Check for tail updates after two hops.
p = (p != t && t != (t = tail)) ? t : q;
}
}
上述的offer(E e)方法,即是向队列尾部添加元素。
首先,入队前构建结点newNode,接下来无限for循环,保证入队成功,所以该方法返回总是true。

假设现在队列中有两个结点a,b,tail指向b。t->tail,p->t,如下图:

假设现在要添加一个c结点,那么会出现下图情况:

如果再添加一个D结点,
(1)因为p结点的后继结点q不为null,并且q也不等于p,所以经过判断后,p指向q,tail不变。如下图:

(2)q继续指向p的next结点,因为q为null,所以把d结点设置为p结点的next结点,因为p不指向t,所以把t指向为尾结点。如下图:

总结下来,我们发现,首先总会获取到尾结点,然后用CAS算法进行结点入队。
ConcurrentLinkedQueue (一)的更多相关文章
- 队列送券的实际应用--ConcurrentLinkedQueue并发队列
1.TicketQueue.java--队列封装类,负责如下职责:a.把活动登记对象放入队列中b.从队列中获取活动登记对象,并派券 package com.datong.pear.ticket; im ...
- 【JUC】JDK1.8源码分析之ConcurrentLinkedQueue(五)
一.前言 接着前面的分析,接下来分析ConcurrentLinkedQueue,ConcurerntLinkedQueue一个基于链接节点的无界线程安全队列.此队列按照 FIFO(先进先出)原则对元素 ...
- Java 线程 — ConcurrentLinkedQueue
ConcurrentLinkedQueue 在考虑并发的时候可以先考虑单线程的情况,然后再将并发的情况考虑进来. 比如ConcurrentLinkedQueue: 先考虑单线的offer 再考虑多线程 ...
- Java多线程系列--“JUC集合”10之 ConcurrentLinkedQueue
概要 本章对Java.util.concurrent包中的ConcurrentHashMap类进行详细的介绍.内容包括:ConcurrentLinkedQueue介绍ConcurrentLinkedQ ...
- [Java 基础] 并发队列ConcurrentLinkedQueue和阻塞队列LinkedBlockingQueue用法
reference : http://www.cnblogs.com/linjiqin/archive/2013/05/30/3108188.html 在Java多线程应用中,队列的使用率很高,多数生 ...
- 并发队列ConcurrentLinkedQueue和阻塞队列LinkedBlockingQueue用法
在Java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列(先进先出).Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是BlockingQ ...
- 阻塞队列LinkedBlockingQueue和并发队列ConcurrentLinkedQueue
LinkedBlockingQueue: public class LinkedBlockingQueue<E> extends AbstractQueue<E> implem ...
- CAS无锁算法与ConcurrentLinkedQueue
CAS:Compare and Swap 比较并交换 java.util.concurrent包完全建立在CAS之上的,没有CAS就没有并发包.并发包借助了CAS无锁算法实现了区别于synchroni ...
- java线程控制、状态同步、volatile、Thread.interupt以及ConcurrentLinkedQueue
在有些严格的系统中,我们需要做到干净的停止线程并清理相关状态.涉及到这个主题会带出很多的相关点,简单的总结如下: 我们知道,在java中,有一个volatile关键字,其官方说明(https://do ...
- java LinkedBlockingQueue和ConcurrentLinkedQueue的区别
实现上看,两者都继承于AbstractQueue,但是ConcurrentLinkedQueue实现了Queue,而LinkedBlockingQueue实现了BlockingQueue,Blocki ...
随机推荐
- Intellij idea 告警:'while' statement cannot complete without throwing an exception
有时候这个告警是多余的,例如我们手写的监控线程. 如果有消除告警强迫症.在线程的执行方法上加入注解. @SuppressWarnings("InfiniteLoopStatement&quo ...
- Java JDBC利用反射技术将查询结果封装为对象
1.JDBC将返回结果集封装成对象demo class JdbcDemo { /** * 获取数据库列名 * @param rs * @return */ private static String[ ...
- Linux 常用操作和命令
腾讯云部署 java web 环境:https://blog.csdn.net/niceLiuSir/article/details/78879844 Tomcat部署和配置:https://blog ...
- openresty开发系列40--nginx+lua实现获取客户端ip所在的国家信息
openresty开发系列40--nginx+lua实现获取客户端ip所在的国家信息 为了实现业务系统针对不同地区IP访问,展示包含不同地区信息的业务交互界面.很多情况下系统需要根据用户访问的IP信息 ...
- Qt编写安防视频监控系统14-本地回放
一.前言 在上一篇文章将视频文件存储好了,需要提供界面方便用户查询视频文件进行回放,其实这个回放就是播放历史存储的视频文件,并不是什么高大上的东西,视频回放在这个系统中分三种,第一种是本地回放,回放存 ...
- k8s记录-pip源配置
#pip源# 清华大学https://pypi.tuna.tsinghua.edu.cn/simple/https://mirrors.tuna.tsinghua.edu.cn/pypi/web/si ...
- 【VS开发】MFC动态设置对话框属性 弹出或嵌入
参考: MFC的对话框CDialog是怎么控制窗口可调整大小的属性的呢?打开资源文件,对话框资源的属性列表中,有一个"Border"项,改变该项的值就可以改变窗口边框风格.实际上w ...
- Maven打包报错:[WARNING] The POM for xxx is missing, no dependency inform
maven install 或 package 时 ,执行警告报错: [WARNING] The POM for com.xx-base:jar:1.0 is missing, no dependen ...
- springboot2 配置 https
package cn.xiaojf.aibus.configure; import org.apache.catalina.Context; import org.apache.catalina.co ...
- python实践项目四:猜数字游戏
题目要求:在1-20中随机生成一个数字,你来猜,只有6次机会. 举例一: #!/usr/bin/python # -*- coding: UTF-8 -*- import random secretN ...