队列是常用的数据结构,采用的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 无锁队列的更多相关文章

  1. CAS简介和无锁队列的实现

    Q:CAS的实现 A:gcc提供了两个函数 bool __sync_bool_compare_and_swap (type *ptr, type oldval, type newval, ...)// ...

  2. 锁、CAS操作和无锁队列的实现

    https://blog.csdn.net/yishizuofei/article/details/78353722 锁的机制 锁和人很像,有的人乐观,总会想到好的一方面,所以只要越努力,就会越幸运: ...

  3. CAS无锁算法与ConcurrentLinkedQueue

    CAS:Compare and Swap 比较并交换 java.util.concurrent包完全建立在CAS之上的,没有CAS就没有并发包.并发包借助了CAS无锁算法实现了区别于synchroni ...

  4. 无锁队列以及ABA问题

    队列是我们非常常用的数据结构,用来提供数据的写入和读取功能,而且通常在不同线程之间作为数据通信的桥梁.不过在将无锁队列的算法之前,需要先了解一下CAS(compare and swap)的原理.由于多 ...

  5. zeromq源码分析笔记之无锁队列ypipe_t(3)

    在上一篇中说到了mailbox_t的底层实际上使用了管道ypipe_t来存储命令.而ypipe_t实质上是一个无锁队列,其底层使用了yqueue_t队列,ypipe_t是对yueue_t的再包装,所以 ...

  6. boost 无锁队列

    一哥们翻译的boost的无锁队列的官方文档 原文地址:http://blog.csdn.net/great3779/article/details/8765103 Boost_1_53_0终于迎来了久 ...

  7. 基于无锁队列和c++11的高性能线程池

    基于无锁队列和c++11的高性能线程池线程使用c++11库和线程池之间的消息通讯使用一个简单的无锁消息队列适用于linux平台,gcc 4.6以上   标签: <无>   代码片段(6)[ ...

  8. java轻松实现无锁队列

    1.什么是无锁(Lock-Free)编程 当谈及 Lock-Free 编程时,我们常将其概念与 Mutex(互斥) 或 Lock(锁) 联系在一起,描述要在编程中尽量少使用这些锁结构,降低线程间互相阻 ...

  9. Erlang运行时中的无锁队列及其在异步线程中的应用

    本文首先介绍 Erlang 运行时中需要使用无锁队列的场合,然后介绍无锁队列的基本原理及会遇到的问题,接下来介绍 Erlang 运行时中如何通过“线程进度”机制解决无锁队列的问题,并介绍 Erlang ...

随机推荐

  1. 【java】抽象类和接口区别

    1.语法层面上的区别 1)抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法: 2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public sta ...

  2. 【mysql】字段类型和长度的解释

    int(11)最大长度是多少,MySQL中varchar最大长度是多少? int(11)最大长度是多少? 在SQL语句中int代表你要创建字段的类型,int代表整型,11代表字段的长度. 这个11代表 ...

  3. 多线程、互斥锁、异步、GIL

    多线程-threading python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便被使用 from threading imp ...

  4. 20175311 2018-2019-2 《Java程序设计》第7周学习总结

    20175311 2018-2019-2 <Java程序设计>第7周学习总结 教材学习内容总结 这一周我主要学习了第八章的内容-常用实用类String类 构造String对象 字符串的并置 ...

  5. 【转】从PowerDesigner概念设计模型(CDM)中的3种实体关系说起

    PowerDesigner概念模型的relationship .inheritance. association 从PowerDesigner概念设计模型(CDM)中的3种实体关系说起

  6. Condtion type Z123 is mandatory!

    user exit: MV45AFZZ->userexit_save_document_prepare ZI_EXIT_SD_CNEMS_SAVE_PREPARE ***Start of ins ...

  7. C/C++中指针和java的引用区别

    C++指针  要区分指针变量和指针变量所指对象. 指针变量先是一个变量,它有自己的地址和存储的内容,所以要想清楚是改变指针变量的值(即地址),还是改变指针变量所指对象的值. #include < ...

  8. LeetCode 789. Escape The Ghosts

    题目链接:https://leetcode.com/problems/escape-the-ghosts/description/ You are playing a simplified Pacma ...

  9. 基于墨刀实现的购物app

    在观察研究购物app后 仔细分析总结了几个基础功能,实现如下的界面: 主界面:购物app的推荐界面,提供各式的样品,将整个app的多种功能进行展示. 推荐分类:根据用户浏览的宝贝,对用户进行合理推荐. ...

  10. DB2调优摘要

    感谢原博主 http://yishueitian326.blog.163.com/blog/static/2858637520106695733719/ 性 能是关系到随需应变型应用程序成功与否的关键 ...