转载自: https://blog.csdn.net/skh2015java/article/details/60334437

golang中sync包实现了两种锁Mutex (互斥锁)和RWMutex(读写锁),其中RWMutex是基于Mutex实现的,只读锁的实现使用类似引用计数器的功能.

type Mutex

    func (m *Mutex) Lock()
    func (m *Mutex) Unlock()
type RWMutex
    func (rw *RWMutex) Lock()
    func (rw *RWMutex) RLock()
    func (rw *RWMutex) RLocker() Locker
    func (rw *RWMutex) RUnlock()

func (rw *RWMutex) Unlock()

其中Mutex为互斥锁,Lock()加锁,Unlock()解锁,使用Lock()加锁后,便不能再次对其进行加锁,直到利用Unlock()解锁对其解锁后,才能再次加锁.适用于读写不确定场景,即读写次数没有明显的区别,并且只允许只有一个读或者写的场景,所以该锁叶叫做全局锁.

func (m *Mutex) Unlock()用于解锁m,如果在使用Unlock()前未加锁,就会引起一个运行错误.

已经锁定的Mutex并不与特定的goroutine相关联,这样可以利用一个goroutine对其加锁,再利用其他goroutine对其解锁.

正常运行例子:

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. func main() {
  7. var l *sync.Mutex
  8. l = new(sync.Mutex)
  9. l.Lock()
  10. defer l.Unlock()
  11. fmt.Println("1")
  12. }
  13. 结果输出:1

当Unlock()在Lock()之前使用时,便会报错

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. func main() {
  7. var l *sync.Mutex
  8. l = new(sync.Mutex)
  9. l.Unlock()
  10. fmt.Println("1")
  11. l.Lock()
  12. }
  13. 运行结果: panic: sync: unlock of unlocked mutex

当在解锁之前再次进行加锁,便会死锁状态

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. func main() {
  7. var l *sync.Mutex
  8. l = new(sync.Mutex)
  9. l.Lock()
  10. fmt.Println("1")
  11. l.Lock()
  12. }
  13. 运行结果:  1
  14. fatal error: all goroutines are asleep - deadlock!

RWMutex是一个读写锁,该锁可以加多个读锁或者一个写锁,其经常用于读次数远远多于写次数的场景.

func (rw *RWMutex) Lock()  写锁,如果在添加写锁之前已经有其他的读锁和写锁,则lock就会阻塞直到该锁可用,为确保该锁最终可用,已阻塞的 Lock 调用会从获得的锁中排除新的读取器,即写锁权限高于读锁,有写锁时优先进行写锁定
  func (rw *RWMutex) Unlock() 写锁解锁,如果没有进行写锁定,则就会引起一个运行时错误.

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. func main() {
  7. var l *sync.RWMutex
  8. l = new(sync.RWMutex)
  9. l.Unlock()
  10. fmt.Println("1")
  11. l.Lock()
  12. }
  13. 运行结果:panic: sync: unlock of unlocked mutex

func (rw *RWMutex) RLock() 读锁,当有写锁时,无法加载读锁,当只有读锁或者没有锁时,可以加载读锁,读锁可以加载多个,所以适用于"读多写少"的场景

func (rw *RWMutex)RUnlock() 读锁解锁,RUnlock 撤销单次 RLock 调用,它对于其它同时存在的读取器则没有效果。若 rw 并没有为读取而锁定,调用 RUnlock 就会引发一个运行时错误(注:这种说法在go1.3版本中是不对的,例如下面这个例子)。

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. func main() {
  7. var l *sync.RWMutex
  8. l = new(sync.RWMutex)
  9. l.RUnlock()    //1个RUnLock
  10. fmt.Println("1")
  11. l.RLock()
  12. }
  13. 运行结果:1
  14. 但是程序中先尝试 解锁读锁,然后才加读锁,但是没有报错,并且能够正常输出.

分析:go1.3版本中出现这种情况的原因分析,通过阅读源码可以很清晰的得到结果

  1. func (rw *RWMutex) RUnlock() {
  2. if raceenabled {
  3. _ = rw.w.state
  4. raceReleaseMerge(unsafe.Pointer(&rw.writerSem))
  5. raceDisable()
  6. }<span style="color:#FF0000;">
  7. if atomic.AddInt32(&rw.readerCount, -1) < 0 { //readercounter初始值为0,调用RUnLock之后变为-1,继续往下执行
  8. // A writer is pending.
  9. if atomic.AddInt32(&rw.readerWait, -1) == 0 { //此时readerwaiter变为1,1-1之后变为0,可以继续以后的操作.</span>
  10. // The last reader unblocks the writer.
  11. runtime_Semrelease(&rw.writerSem)
  12. }
  13. }
  14. if raceenabled {
  15. raceEnable()
  16. }
  17. }

当RUnlock多于RLock多个时,便会报错,进入死锁.实例如下:

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. type s struct {
  7. readerCount int32
  8. }
  9. func main() {
  10. l := new(sync.RWMutex)
  11. l.RUnlock()
  12. l.RUnlock()        //此处出现死锁
  13. fmt.Println("1")
  14. l.RLock()
  15. }
  16. 运行结果:
  17. 1
  18. fatal error: all goroutines are asleep - deadlock!

总结:

所以在go1.3版本中,运行过程中允许RUnLock早于RLock一个,也只能早于1个(注:虽然代码允许,但是强烈不推荐使用),并且在早于之后必须利用RLock进行加锁才可以继续使用.

go Mutex (互斥锁)和RWMutex(读写锁)的更多相关文章

  1. Linux 自旋锁,互斥量(互斥锁),读写锁

    自旋锁(Spin Lock) 自旋锁类似于互斥量,不过自旋锁不是通过休眠阻塞进程,而是在取得锁之前一直处于忙等待的阻塞状态.这个忙等的阻塞状态,也叫做自旋. 自旋锁通常作为底层原语实现其他类型的锁. ...

  2. C# 多线程编程之锁的使用【互斥锁(lock)和读写锁(ReadWriteLock)】

    多线程编程之锁的使用[互斥锁(lock)和读写锁(ReadWriteLock)] http://blog.csdn.net/sqqyq/article/details/18651335 多线程程序写日 ...

  3. RWLock——一种细粒度的Mutex互斥锁

    RWMutex -- 细粒度的读写锁 我们之前有讲过 Mutex 互斥锁.这是在任何时刻下只允许一个 goroutine 执行的串行化的锁.而现在这个 RWMutex 就是在 Mutex 的基础上进行 ...

  4. golang RWMutex读写锁分析

    RWMutex:是基于Mutex实现的读写互斥锁,一个goroutine可以持有多个读锁或者一个写锁,同一时刻只能持有读锁或者写锁 数据结构设计: type RWMutex struct { w Mu ...

  5. golang mutex互斥锁分析

    互斥锁:没有读锁写锁之分,同一时刻,只能有一个gorutine获取一把锁 数据结构设计: type Mutex struct { state int32 // 将一个32位整数拆分为 当前阻塞的gor ...

  6. Go 标准库 —— sync.Mutex 互斥锁

    Mutex 是一个互斥锁,可以创建为其他结构体的字段:零值为解锁状态.Mutex 类型的锁和线程无关,可以由不同的线程加锁和解锁. 方法 func (*Mutex) Lock func (m *Mut ...

  7. 多线程并发编程之显示锁ReentrantLock和读写锁

    在Java5.0之前,只有synchronized(内置锁)和volatile. Java5.0后引入了显示锁ReentrantLock. ReentrantLock概况 ReentrantLock是 ...

  8. JUC——线程同步锁(ReentrantReadWriteLock读写锁)

    读写锁简介 所谓的读写锁值得是两把锁,在进行数据写入的时候有一个把“写锁”,而在进行数据读取的时候有一把“读锁”. 写锁会实现线程安全同步处理操作,而读锁可以被多个对象读取获取. 读写锁:ReadWr ...

  9. 锁的封装 读写锁、lock

    最近由于项目上面建议使用读写锁,而去除常见的lock锁.然后就按照需求封装了下锁.以简化锁的使用.但是开发C#的童鞋都知道lock关键字用起太方便了,但是lock关键字不支持超时处理.很无奈,为了实现 ...

  10. C# Mutex互斥锁

    Mutex 构造函数 (Boolean, String, Boolean) public Mutex ( bool initiallyOwned, string name, out bool crea ...

随机推荐

  1. UNIX环境高级编程——线程私有数据

    线程私有数据(Thread-specific data,TSD):存储和查询与某个线程相关数据的一种机制. 在进程内的所有线程都共享相同的地址空间,即意味着任何声明为静态或外部变量,或在进程堆声明的变 ...

  2. android开发要避免的那些坑

    SparseArray 目前有很多地方从性能优化方说使用SparseArray来替换hashMap,来节省内存,提高性能. Linkify.addLinks() 这个类可以更方便的为文本添加超链接. ...

  3. MO_GLOBAL - EBS R12 中 Multi Org 设计的深入研究 (2)

    这是多组织访问的第二篇文章,翻译自Anil Passi的Multi Org R12 我们都知道,在Oracle Release 12中多组织模型(Multi Org)会被改变, 它被叫作多组织访问控制 ...

  4. Python基础:条件判断与循环的两个要点

    一.条件判断: Python中,条件判断用if语句实现,多个条件判断时用if...elif实现:看下面一段程序 #python 3.3.5 #test if...elif age = 20 if ag ...

  5. AngularJS中的依赖注入

    依赖注入 | Dependency Injection 原文链接: Angular Dependency Injection翻译人员: 铁锚翻译时间: 2014年02月10日说明: 译者认为,本文中所 ...

  6. unity UGUI实现类似NGUI切换Sprite的方式

    很多都是使用NGUI的习惯,因为在NGUI中所有图片都打包在一个图集中,通过更改SpriteName就可以更改图片,so,为了方便调用UGUI的sprite,我们也同样需要为其创建一个asset文件. ...

  7. java中throw与throws

    类或函数声明,用throws表示,函数体中,可能抛出的错误,后接多个. 1.系统自动抛出的异常 系统定义的编译和运行异常都可以由系统自动抛出,称为标准异常. 2.语句抛出的异常 用户程序自定义的异常和 ...

  8. 解决ADT大量出现"Unexpected value from nativeGetEnabledTags: 0"的问题

    在过滤器中输入下面的代码即可:(此方法搜索于网略) ^(?!.*(nativeGetEnabledTags)).*$ 

  9. *** non-numeric second argument to `wordlist' function: ''. Stop错误解决办法

    PS: 解决办法搜集自:stackoverflow website:http://stackoverflow.com/questions/5677178/ndk-gdb-fails-with-mess ...

  10. linux socket编程之TCP与UDP

    转:http://blog.csdn.net/gaoxin1076/article/details/7262482 TCP/IP协议叫做传输控制/网际协议,又叫网络通信协议 TCP/IP虽然叫传输控制 ...