转载自: 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. 手把手带你走进MVP +Dagger2 + DataBinding+ Rxjava+Retrofit 的世界

    0.0 Android开发现在的变化用一个词来形容就是 :翻天覆地 越来越多的项目使用了MVP + Rxjava+Retrofit +Dagger2 + DataBinding等等东西.. 但是这些东 ...

  2. JAVA之旅(二十四)——I/O流,字符流,FileWriter,IOException,文件续写,FileReader,小练习

    JAVA之旅(二十四)--I/O流,字符流,FileWriter,IOException,文件续写,FileReader,小练习 JAVA之旅林林总总也是写了二十多篇了,我们今天终于是接触到了I/O了 ...

  3. MySQL 数据库开发的 36 条军规

    MySQL 数据库开发的 36 条军规 写在前面的话: 总是在灾难发生后,才想起容灾的重要性: 总是在吃过亏后,才记得曾经有人提醒过. (一)核心军规 (1)不在数据库做运算:cpu计算务必移至业务层 ...

  4. python字符串与数字类型转化

    数字转字符串:str(数字),如str(10) 相反:int(字符串),如int('10') 另外,import string后 用string.atoi('100',base),转换为int,bas ...

  5. Linux多线程实践(9) --简单线程池的设计与实现

    线程池的技术背景 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源.在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收.所以 ...

  6. javascript之数组对象与数组常用方法

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  7. android 签名

    (1)Android Studio菜单Build->Generate Signed APK (2)弹出窗口 (3)创建密钥库及密钥,创建后会自动选择刚创建的密钥库和密钥(已拥有密钥库跳过)    ...

  8. ssh连接原理介绍( 无密码连接登录的原理)

    SSH(Secure  Shell)一种在不安全网络上提供安全远程登录及其它安全网络服务的协议.由客户端和服务端的软件组成的,有两个不兼容的版本分别是:1.x和2.x.(SSH 2.x的客户程序是不能 ...

  9. java容易混淆的15个知识点

    java知识点不多,但是有一些经常会被我们忽略 1.java是强类型的语言,数组也是对象,一旦确定数组的类型,里面就只能存放一个类型的数据. 2.新建的对象都被存放到堆上,如果没有引用,会很快垃圾回收 ...

  10. python的安装,IDLE基本操作

    §一.安装Python 1. 下载Active Python安装包 根据你机器型号download Python,32b选择for windows X86,64b选择for window 64b 2. ...