CAS 无锁队列
队列是常用的数据结构,采用的FIFO(first in firstout)原则,新元素(等待进入队列的元素)总是被插入到尾部,而读取的时候总是从头部开始读取。在计算中队列一般用来做排队(如线程池的等待排队,锁的等待排队),用来做解耦(生产者消费者模式),异步等等。在java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列。队列在现实生活中也很常见,例如去超市买东西排队付钱,先来的先付账走人回家,后来的要等前面的人付完钱才能付账。
首先我们看一段队列代码:

type Queue struct {
head unsafe.Pointer
tail unsafe.Pointer
Reset func(interface{})
New func() interface{}
}
// one node in queue
type Node struct {
val interface{}
next unsafe.Pointer
}
func QueueNew()(*Queue){
queue := new(Queue)
queue.head = unsafe.Pointer(new(Node))
queue.tail = queue.head
return queue
}
func (self *Queue) EnQueue(val interface{}) {
if self.Reset!= nil{
self.Reset(val)
}
newNode := unsafe.Pointer(&Node{val: val, next: nil})
var tail, next unsafe.Pointer
tail = self.tail
((*Node)(tail)).next = newNode
self.tail = newNode
}
func (self *Queue) DeQueue() (val interface{}) {
var head, tail, next unsafe.Pointer
head = self.head
tail = self.tail
next = ((*Node)(head)).next
if head == tail {
// There's no data in the queue.
return nil
}
val = ((*Node)(next)).val
self.head = next
return val
}
这是一般的队列实现方法,适用于单线程但如果是多线程操作就麻烦了。例如在超市柜台结账,大家都按规则进行排队没有问题,但是如果有两个人张大妈和李大妈都着急结账回家接孙子,同时跑到了同一个队列的队尾,她们两都说自己应该排在队尾。那么问题就来了。那么对于多线程操作同一个队列,可以用锁的方法来实现,在入队和出队前加上锁即可:
type Queue struct {
sync.RWMutex
head unsafe.Pointer
tail unsafe.Pointer
Reset func(interface{})
New func() interface{}
}
func (self *Queue) EnQueue(val interface{}) {
self.Lock()
defer self.Unlock()
if self.Reset != nil {
self.Reset(val)
}
newNode := unsafe.Pointer(&Node{val: val, next: nil})
var tail, next unsafe.Pointer
tail = self.tail
((*Node)(tail)).next = newNode
self.tail = newNode
}
func (self *Queue) DeQueue() (val interface{}) {
var head, tail, next unsafe.Pointer
self.Lock()
defer self.Unlock()
head = self.head
tail = self.tail
next = ((*Node)(head)).next
if head == tail {
// There's no data in the queue.
return nil
}
val = ((*Node)(next)).val
self.head = next
return val
}
但是,这种加锁的方法在多进程的操作中会消耗很多系统资源,使用不当还会造成死锁,下面推荐一种CAS的方法来实现队列的安全出队和入队。CAS(Compare and Swap),比较并交换,在大多数处理器架构,CAS的具体是判断一个内存上的数据是否是所判断的值,如果是,那么执行修改;如果不是,那么将不做操作并返回当前值。CAS是一种乐观锁,多线程执行过程中,多个线程去修改内存中的数据,有且只有一个能修改成功,但是失败的线程不会中断或者挂起。具体代码如下:
func (self *Queue) EnQueue(val interface{}) {
if self.Reset!= nil{
self.Reset(val)
}
newNode := unsafe.Pointer(&Node{val: val, next: nil})
var tail, next unsafe.Pointer
for {
tail = self.tail
next = ((*Node)(tail)).next
if tail != self.tail{
runtime.Gosched()
continue
} //[PositionA]-----------A new node may already enqueue-------------
if next != nil {
atomic.CompareAndSwapPointer(&(self.tail), tail, next)
continue
}
if atomic.CompareAndSwapPointer(&((*Node)(tail).next), nil,newNode ) {
break
}
runtime.Gosched()
}
atomic.CompareAndSwapPointer(&(self.tail),tail, newNode)
}
func (self *Queue) DeQueue() (val interface{}) {
var head, tail, next unsafe.Pointer
for {
head = self.head
tail = self.tail
next = ((*Node)(head)).next
if head != self.head{
runtime.Gosched()
continue
}
if next == nil{
if self.New != nil{
return self.New()
}else{
return nil
}
}
if head == tail {
atomic.CompareAndSwapPointer(&(self.tail), tail, next)
}else{
val = ((*Node)(next)).val //[PositionB]---------The head node may already Dequeue---------
if atomic.CompareAndSwapPointer(&(self.head), head, next) {
return val
}
}
runtime.Gosched()
}
}
多线程在运行这段代码的过程中可能在位置A和位置B发生抢占,所以要先进行比较,如果一样再进行操作,这样就能保证一致性。
CAS 无锁队列的更多相关文章
- CAS简介和无锁队列的实现
Q:CAS的实现 A:gcc提供了两个函数 bool __sync_bool_compare_and_swap (type *ptr, type oldval, type newval, ...)// ...
- 锁、CAS操作和无锁队列的实现
https://blog.csdn.net/yishizuofei/article/details/78353722 锁的机制 锁和人很像,有的人乐观,总会想到好的一方面,所以只要越努力,就会越幸运: ...
- CAS无锁算法与ConcurrentLinkedQueue
CAS:Compare and Swap 比较并交换 java.util.concurrent包完全建立在CAS之上的,没有CAS就没有并发包.并发包借助了CAS无锁算法实现了区别于synchroni ...
- 无锁队列以及ABA问题
队列是我们非常常用的数据结构,用来提供数据的写入和读取功能,而且通常在不同线程之间作为数据通信的桥梁.不过在将无锁队列的算法之前,需要先了解一下CAS(compare and swap)的原理.由于多 ...
- zeromq源码分析笔记之无锁队列ypipe_t(3)
在上一篇中说到了mailbox_t的底层实际上使用了管道ypipe_t来存储命令.而ypipe_t实质上是一个无锁队列,其底层使用了yqueue_t队列,ypipe_t是对yueue_t的再包装,所以 ...
- boost 无锁队列
一哥们翻译的boost的无锁队列的官方文档 原文地址:http://blog.csdn.net/great3779/article/details/8765103 Boost_1_53_0终于迎来了久 ...
- 基于无锁队列和c++11的高性能线程池
基于无锁队列和c++11的高性能线程池线程使用c++11库和线程池之间的消息通讯使用一个简单的无锁消息队列适用于linux平台,gcc 4.6以上 标签: <无> 代码片段(6)[ ...
- java轻松实现无锁队列
1.什么是无锁(Lock-Free)编程 当谈及 Lock-Free 编程时,我们常将其概念与 Mutex(互斥) 或 Lock(锁) 联系在一起,描述要在编程中尽量少使用这些锁结构,降低线程间互相阻 ...
- Erlang运行时中的无锁队列及其在异步线程中的应用
本文首先介绍 Erlang 运行时中需要使用无锁队列的场合,然后介绍无锁队列的基本原理及会遇到的问题,接下来介绍 Erlang 运行时中如何通过“线程进度”机制解决无锁队列的问题,并介绍 Erlang ...
随机推荐
- 【转】搭建和配置Syncthing发现和中继服务器
搭建和配置Syncthing发现和中继服务器 折腾借口 Syncthing好是挺好的,但就是同步不给力,公共网络服务其质量参差不齐,网络也说不清.最好的解决方案还是自己搭建发现服务器Synct ...
- 代码漏洞扫描描述Cross Site History Manipulation解决办法[dongcoder.com]
代码漏洞扫描 漏洞描述:Cross Site History Manipulation 简要描述:产品的行为差异或发送不同的反应,在某种程度上暴露了与安全性相关的产品状态,例如特定的操作是否成功.可能 ...
- onclick事件传递对象参数
<a href="#"onclick="editName(JSON.stringify(data).replace(/"/g, '"'))&qu ...
- RN 的页面布局
从 https://blog.csdn.net/liangzelei/article/details/53965417转载 React Native布局详细指南 https://www.jiansh ...
- 【linux】常用命令之用户管理
查看用户 cat /etc/passwd 查看某一个用户 cat /etc/passwd|grep javaluna 新增用户 useradd xxx 更新密码 passwd xxx 删除用户及相关配 ...
- MD5 Hashing in Java
参考: MD5 Hashing in Java
- HTML怎么实现字体加粗
HTML的加粗标签是<b>标签,是用来对你自定文字加粗,写法如下: 字体加粗:<b>这里是加粗的内容</b> 这样就可以实现加粗了!
- JAVA之Mybatis基础入门二 -- 新增、更新、删除
上一节说了Mybatis的框架搭建和简单查询,这次我们来说一说用Mybatis进行基本的增删改操作: 一. 插入一条数据 1.首先编写USER.XML(表的xml)使用insert元素,元素写在map ...
- SpringMVC Controller接收前台ajax的GET或POST请求返回各种参数
这几天写新项目遇到这个问题,看这位博主总结得不错,懒得写了,直接转!原文:http://blog.csdn.net/yixiaoping/article/details/45281721原文有些小错误 ...
- Angular Beijing 发布
为了帮助 Angular 在国内的推广,申请了一个新的域名 www.ngbeijing.cn, 我将 Angular 相关的优秀文章集中在这个站点,欢迎大家访问. 刚刚转载了几篇优秀的文章. Ang ...