当前位置:网站首页>如何使用ConcurrentLinkedQueue做一个缓存队列
如何使用ConcurrentLinkedQueue做一个缓存队列
2022-07-04 20:34:00 【小曲同学呀】
在项目中,我们可能会用到队列,那么ConcurrentLinkedQueue,是必不可少的一个。对于多线程环境下,想要实现线程安全,要么自己加锁,要么利用线程安全队列-------ConcurrentLinkedQueue。
先看一下使用ConcurrentLinkedQueue是如何做一个缓存队列的。
package server.utils;
import java.util.concurrent.ConcurrentLinkedQueue;
public class QueueUtils {
//用户缓存队列
private static final ConcurrentLinkedQueue<String> queueCache = new ConcurrentLinkedQueue<>();
private int rank;
//入队
public void offer(String userId) {
queueCache.offer(userId);
}
//判断元素是否存在
public boolean isExist(String userId) {
if (queueCache.contains(userId)) {
return true;
}
return false;
}
//获取排名
public int getRank(String userId) {
if (isExist(userId)) {
for (String id : queueCache) {
if (id.equals(userId)) {
break;
}
rank++;
}
System.out.printf("rank排名为" + rank);
return rank;
} else {
//不包括则加入队尾
offer(userId);
rank = queueCache.size() + 1;
System.out.printf("不包含用户则加入队尾" + rank);
return rank;
}
}
public static void main(String[] args) {
for (int i = 1; i < 6; i++) {
new QueueUtils().getRank("1" + i);
}
new QueueUtils().getRank("15");
}
}

总结一下常用的三个方法:peak(),poll()方法,offer()方法
peak()方法:
这个方法的作用就是获取队列头部的元素,只获取不移除,注意这个方法和下面的poll方法的区别啊!
public E peek() {
//[1]goto标志
restartFromHead:
for (;;) {
for (Node<E> h = head, p = h, q;;) {
//[2]
E item = p.item;
//[3]
if (item != null || (q = p.next) == null) {
updateHead(h, p);
return item;
}
//[4]
else if (p == q)
continue restartFromHead;
else//[5]
p = q;
}
}
}
final void updateHead(Node<E> h, Node<E> p) {
if (h != p && casHead(h, p)) {
h.lazySetNext(h);
}
poll()方法:
这个方法是获取头部的这个节点,如果队列为空则返回null;
public E poll() {
//这里其实就是一个goto的标记,用于跳出for循环
restartFromHead:
//[1]
for (;;) {
for (Node<E> h = head, p = h, q;;) {
E item = p.item;
//[2]如果当前节点中存的值不为空,则CAS设置为null
if (item != null && p.casItem(item, null)) {
//[3]CAS成功就更新头节点的位置
if (p != h)
updateHead(h, ((q = p.next) != null) ? q : p);
return item;
}
//[4]当前队列为空,就返回null
else if ((q = p.next) == null) {
updateHead(h, p);
return null;
}
//[5]当前节点和下一个节点一样,说明节点自引用,则重新找头节点
else if (p == q)
continue restartFromHead;
//[6]
else
p = q;
}
}
}
final void updateHead(Node<E> h, Node<E> p) {
if (h != p && casHead(h, p))
h.lazySetNext(h);
}
offer()方法:
这个方法的作用就是在队列末端添加一个节点,如果传递的参数是null,就抛出空指针异常,否则由于该队列是无界队列,该方法会一直返回true,而且该方法使用CAS算法实现的,所以不会阻塞线程;
//队列末端添加一个节点
public boolean offer(E e) {
//如果e为空,那么抛出空指针异常
checkNotNull(e);
//将传进来的元素封装成一个节点,Node的构造器中调用UNSAFE.putObject(this, itemOffset, item)把e赋值给节点中的item
final Node<E> newNode = new Node<E>(e);
//[1]
//这里的for循环从最后的节点开始
for (Node<E> t = tail, p = t;;) {
Node<E> q = p.next;
//[2]如果q为null,说明p就是最后的节点了
if (q == null) {
//[3]CAS更新:如果p节点的下一个节点是null,就把写个节点更新为newNode
if (p.casNext(null, newNode)) {
//[4]CAS成功,但是这时p==t,所以不会进入到这里的if里面,直接返回true
//那么什么时候会走到这里面来呢?其实是要有另外一个线程也在调用offer方法的时候,会进入到这里面来
if (p != t)
casTail(t, newNode);
return true;
}
}
else if (p == q) //[5]
p = (t != (t = tail)) ? t : head;
else //[6]
p = (p != t && t != (t = tail)) ? t : q;
}
}
总结:
其实还有几个方法没说,但是感觉比较容易就不浪费篇幅了,有兴趣的可以看看:size方法用于计算队列中节点的数量,可是由于没有加锁,在并发的条件下不准确;remove方法删除某个节点,其实就是遍历然后用equals方法比较item是不是一样,只不过如果存在多个符合条件的节点只删除第一个,然后返回true,否则返回false;contains方法判断队列中是否包含指定item的节点,也就是遍历,很容易;
最麻烦的就是offer方法和poll方法,offer方法是在队列的最后面添加节点,而poll是获取头节点,并且删除第一个真正的队列节点(注意,节点分为两种,一种是哨兵节点,一种是真正的存了数据的节点啊),还简单的说了一下poll方法和peek方法的区别,后者只是获取,而不删除啊!
边栏推荐
- D3.js+Three.js数据可视化3d地球js特效
- colResizable.js自动调整表格宽度插件
- 学习突围3 - 关于精力
- 冰河的海报封面
- redis发布订阅的使用
- PS vertical English and digital text how to change direction (vertical display)
- Jerry's ad series MIDI function description [chapter]
- 杰理之AD 系列 MIDI 功能说明【篇】
- Jerry's ad series MIDI function description [chapter]
- The video sound of station B is very low - solution
猜你喜欢

PS vertical English and digital text how to change direction (vertical display)
![Jerry's ad series MIDI function description [chapter]](/img/28/e0f9b41db597ff3288af431c677001.png)
Jerry's ad series MIDI function description [chapter]

IIC (STM32)

Difference between ApplicationContext and beanfactory (MS)

杰理之AD 系列 MIDI 功能说明【篇】

巅峰不止,继续奋斗!城链科技数字峰会于重庆隆重举行

Maidong Internet won the bid of Beijing life insurance

redis03——Redis的网络配置与心跳机制

Huawei ENSP simulator enables devices of multiple routers to access each other

Configuration of DNS server of Huawei ENSP simulator
随机推荐
多模输入事件分发机制详解
redis布隆过滤器
Kubeadm初始化报错:[ERROR CRI]: container runtime is not running
Solution of 5g unstable 5g signal often dropped in NetWare r7000 Merlin system
PS vertical English and digital text how to change direction (vertical display)
NetWare r7000 Merlin system virtual memory creation failed, prompting that the USB disk reading and writing speed does not meet the requirements. Solution, is it necessary to create virtual memory??
测试用例 (TC)
Actual combat simulation │ JWT login authentication
Jerry's ad series MIDI function description [chapter]
What are the functional modules of RFID warehouse management system solution
Stealing others' vulnerability reports and selling them into sidelines, and the vulnerability reward platform gives rise to "insiders"
D3.js+Three.js数据可视化3d地球js特效
redis发布订阅的使用
Introduction to pressure measurement of JMeter
Gobang go to work fishing tools can be LAN / man-machine
Jerry's ad series MIDI function description [chapter]
[solution] paddlepaddle 2 X call static graph mode
【optimtool.unconstrain】无约束优化工具箱
Jerry's ad series MIDI function description [chapter]
CAD中能显示打印不显示