1.    引言

在并发编程中我们有时候需要使用线程安全的队列。

如果我们要实现一个线程安全的队列有两种实现方式:一种是使用阻塞算法,另一种是使用非阻塞算法。

使用阻塞算法的队列可以用一个锁(入队和出队用同一把锁)或两个锁(入队和出队用不同的锁)等方式来实现,

非阻塞的实现方式则可以使用循环CAS的方式来实现,本文让我们一起来研究下如何使用非阻塞的方式来实现线程安全队列ConcurrentLinkedQueue的。

2.    ConcurrentLinkedQueue的介绍

ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,

当我们添加一个元素的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。

入队列就是将入队节点添加到队列的尾部

tail.casNext(null, n)   CAS算法,往tail的Next 设入队节点n。

tail.casTail(tail, n)   CAS算法,把入队节点n 标记为tail 。

01 public boolean offer(E e) {
02  
03        if (e == null)
04  
05          throw new NullPointerException();
06  
07       Node</e><e> n = new Node</e><e>(e);
08  
09       for (;;) {
10  
11          Node</e><e> t = tail;
12  
13          if (t.casNext(null, n) && casTail(t, n)) {
14  
15             return true;
16  
17          }
18  
19       }
20  
21     }

这个方法没有任何锁操作。线程安全完全由CAS操作和队列的算法来保证。整个算法的核心是for循环,这个循环没有出口,直到尝试成功,这也符合CAS操作的流程。

ConcurrentLinkedQueue使用时,应当注意的地方:

查看ConcurrentLinkedQueue的API ,

.size() 是要遍历一遍集合的,性能慢,所以尽量要避免用size而改用 isEmpty()

ConcurrentLinkedQueue并发队列的一个demo:
先建立一个测试pojo,Log.java两个字段,加上get、set方法
private Date date;
private String value;

然后建立队列类,如下:
/**
 * 用于记录日志的队列,ConcurrentLinkedQueue <br/>
 * 此队列按照 FIFO(先进先出)原则对元素进行排序,详见J2SE_API或JDK
 * @author RSun
 * 2012-2-22下午05:05:19
 */
public class SystemLogQueue {

private static Queue<Log> log_Queue;
static{
if (null == log_Queue) {
log_Queue = new ConcurrentLinkedQueue<Log>(); //基于链接节点的无界线程安全队列
}
}

/** 初始化创建队列 **/
public static void init() {
if (null == log_Queue) {
log_Queue = new ConcurrentLinkedQueue<Log>(); //基于链接节点的无界线程安全队列
}
}

/**
* 添加到队列方法,将指定元素插入此队列的尾部。
* @param log Log对象
* @return 成功返回true,否则抛出 IllegalStateException
*/
public static boolean add(Log log) {
return (log_Queue.add(log));//由于是无界队列(21亿个元素),基本上可以保证一直添加
}
/** 获取并移除此队列的头 ,如果此队列为空,则返回 null */
public static Log getPoll() {
return (log_Queue.poll());
}
/** 获取但不移除此队列的头;如果此队列为空,则返回 null **/
public static Log getPeek() {
return (log_Queue.peek());
}
/** 判断此队列是否有元素 ,没有返回true **/
public static boolean isEmpty() {
return (log_Queue.isEmpty());
}
/** 获取size,速度比较慢 **/
public static int getQueueSize() {
return (log_Queue.size());
}

public static void main(String[] args) {
System.out.println("队列是否有元素:" + !isEmpty());
Log log = new Log();
log_Queue.add(log);

System.out.println("队列是否有元素:" + !isEmpty());
Log log2 = new Log();
log2.setDate(new Date());
log2.setValue("哈哈哈");
log_Queue.add(log2);
System.out.println("队列元素个数:" + getQueueSize());

Log l = getPeek();
System.out.println("\n获取队列数据:" + l.getValue() + "---" + l.getDate());
System.out.println("队列元素个数:" + getQueueSize());

for (int i = 0; i < 2; i++) {
Log l2 = getPoll();
if(l2 != null){
System.out.println("\n获取队列数据并删除:" + l2.getValue() + "---" + l2.getDate());
}
System.out.println("队列元素个数:" + getQueueSize());
}
结果:
// 队列是否有元素:false
// 队列是否有元素:true
// 队列元素个数:2
// 获取队列数据:null---null
// 队列元素个数:2
// 获取队列数据并删除:null---null
// 队列元素个数:1
// 获取队列数据并删除:哈哈哈---Thu Nov 15 13:58:02 CST 2012
// 队列元素个数:0
}
}

深入理解java:2.3.4. 并发编程concurrent包 之容器ConcurrentLinkedQueue(非阻塞的并发队列---循环CAS)的更多相关文章

  1. 深入理解java:2.3.5. 并发编程concurrent包 之容器BlockingQueue(阻塞队列)

    1. 什么是阻塞队列? 阻塞队列(BlockingQueue)是一个支持两个附加操作的队列. 这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空. 当队列满时,存储元素的线程会等待队列 ...

  2. 深入理解java:2.3.3. 并发编程concurrent包 之容器ConcurrentHashMap

    线程不安全的HashMap 因为多线程环境下,使用Hashmap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap. 效率低下的HashTable容器 H ...

  3. Python并发编程-concurrent包

    Python并发编程-concurrent包 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.concurrent.futures包概述 3.2版本引入的模块. 异步并行任务编程 ...

  4. 深入理解java:2.3.2. 并发编程concurrent包 之重入锁/读写锁/条件锁

    重入锁 Java中的重入锁(即ReentrantLock)   与JVM内置锁(即synchronized)一样,是一种排它锁. ReentrantLock提供了多样化的同步,比如有时间限制的同步(定 ...

  5. java多线程 --ConcurrentLinkedQueue 非阻塞 线程安全队列

    ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部:当我们获取一个元素时,它会返回队列头 ...

  6. 深入理解java:2.3.6. 并发编程concurrent包 之管理类---线程池

    我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁 ...

  7. 深入理解java:2.3.1. 并发编程concurrent包 之Atomic原子操作(循环CAS)

    java中,可能有一些场景,操作非常简单,但是容易存在并发问题,比如i++, 此时,如果依赖锁机制,可能带来性能损耗等问题, 于是,如何更加简单的实现原子性操作,就成为java中需要面对的一个问题. ...

  8. 并发编程:协程TCP、非阻塞IO、多路复用、

    一.线程池实现阻塞IO 二.非阻塞IO模型 三.多路复用,降低CPU占用 四.模拟异步IO 一.线程池实现阻塞IO 线程阻塞IO 客户端 import socket c = socket.socket ...

  9. 【Java并发编程】6、volatile关键字解析&内存模型&并发编程中三概念

    volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以 ...

随机推荐

  1. hiho #1144 : 01串(模拟)

    #1144 : 01串 时间限制:7000ms 单点时限:1000ms 内存限制:256MB 描述 给定两个整数n和m,求是否存在恰好包含n个0和m个1的01串S,使得S中不存在子串"001 ...

  2. Open Cascade:计算体积

    TopoDS_Shape shape; ... GProp_GProps System; BRepGProp::LinearProperties(shape, System); BRepGProp:: ...

  3. 51nod 1120 机器人走方格V3

    1120 机器人走方格 V3  基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题  收藏  关注 N * N的方格,从左上到右下画一条线.一个机器人从左上走到右下,只 ...

  4. jmxtrans + influxdb + granafa 监控套件使用手册

    需求说明 随着大数据组件的日益完善,需要随时随地保持各个组件的日常运行,对各个组件的监控势在必行.为了减少运维部门的负担,通过筛选,我们使用 jmxtrans + influxdb + granafa ...

  5. oracle条件参数中 IN函数中的值最大只能为1000个

    delete from dep where id in(1,2,3.....) 括号里面字段个数最大只能为1000个

  6. C++入门经典-例6.20-修改string字符串的单个字符

    1:使用+可以将两个string 字符串连接起来.同时,string还支持标准输入输出函数.代码如下: // 6.20.cpp : 定义控制台应用程序的入口点. // #include "s ...

  7. C++入门经典-例3.5-判断某一年是否是闰年之嵌套判断

    1:代码如下: // 3.5.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> using ...

  8. Java JDBC 基础

    JDBC API 包含以下几个核心部分: 1:JDBC 驱动 2:Connections (连接) 3:Statements (声明) 4:Result Sets (结果集) JDBC: 打开数据库连 ...

  9. spark MLlib 概念 4: 协同过滤(CF)

    1. 定义 协同过滤(Collaborative Filtering)有狭义和广义两种意义: 广义协同过滤:对来源不同的数据,根据他们的共同点做过滤处理. Collaborative filterin ...

  10. elk5.0 版本遇到的安装问题

    问题1:max_map_count不够大 max virtual memory areas vm.max_map_count [65536] likely too low, increase to a ...