队列是常用的数据结构,采用的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 ...
随机推荐
-
J2EE与JavaWeb的区别
1.Java分类 Java分为JavaSE(Java标准版).J2EE(Java企业版)和JavaME(Java微型版): JavaSE(Java Standard Edition),一般用来开发桌面 ...
-
Apple Developer Program Roles Overview
Apple Developer Program Roles Overview There are three roles that can be assigned to Apple Developer ...
-
说明&;总目录
1. 说明 1.1 这是一个乱七八糟的博客,包含遇到的各类问题,甚至会有奇♂怪的东西~ 1.2 作者目前本科生,懒虫一只,喜欢吃喝玩乐看动漫,更喜欢睡觉 1.3 文章难免有错,欢迎指出 1.4 语死早 ...
-
Java多线程(三) 多线程间的基本通信
多条线程在操作同一份数据的时候,一般需要程序去控制好变量.在多条线程同时运行的前提下控制变量,涉及到线程通信及变量保护等. 本博文主要总结:①线程是如何通信 ②如何保护线程变量 1.Java里的线程 ...
-
文件队列 QueueFile
/** * Copyright (C) 2010 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the " ...
-
Leetcode题1
Given an array of integers, find two numbers such that they add up to a specific target number. The ...
-
免费视频播放器videojs中文教程
Video.js是一款web视频播放器,支持html5和flash两种播放方式.更多关于video.js的介绍,可以访问官方网站介绍,我之前也写过一篇关于video.js的使用心得,有兴趣的可以点这里 ...
-
Bzoj 4950
Description 那是春日里一个天气晴朗的好日子,你准备去见见你的老朋友Patrick,也是你之前的犯罪同伙.Patrick在编程竞赛上豪赌输掉了一大笔钱,所以他需要再干一票.为此他需要你的帮助 ...
-
mysql 多表删除
删除用户数据,我们就需要删除有关用户的所有数据. 主表是有数据的,其他关联表不一定有数据,我们可以用left join 来关联删除的表. eg:table1 是主表,t2,t3是关联表. SELECT ...
-
论一个蒟蒻的脑子里可以有多少坑(貌似咕了……目前更新保持在noip阶段)
就是错题整理了,其实也会把一些不该犯的失误整进来. 其实之前一直拖着不想写,直到某次模拟赛,看错了2道题,顺便爆了一道题的int(没错第一个点就会爆)之后爆零了,吓得我赶紧把这篇博客搞出来了..... ...