在golang中,实现了两种锁:
- Mutex,互斥锁
- RWMutex:读写锁,RWMutex基于Mutex实现
Mutex(互斥锁)
- Mutex为互斥锁,Lock()加锁,Unlock()解锁
- 在一个goroutine获得Mutex之后,其他goroutine只能等到这个goroutine释放该Mutex
- 使用Lock()加锁之后,不能继续对其加锁,知道利用Unlock()解锁之后才能解锁
- 在Lock()之前使用Unlock()会导致panic异常
- 已经锁定的Mutex并不与特定的goroutine相关联,这样可以利用一个goroutine对其加锁,再利用其他groutine对其解锁
- 适用于读写不确定,并且只有一个读或者写的场景
加锁和解锁示例
package mainimport ("time""fmt""sync")func main() {var mutex sync.Mutexfmt.Println("Lock the lock")mutex.Lock()fmt.Println("The lock is locked")channels := make([]chan int, 4)for i := 0; i < 4; i++ {channels[i] = make(chan int)go func(i int, c chan int) {fmt.Println("Not lock: ", i)mutex.Lock()fmt.Println("Locked: ", i)time.Sleep(time.Second)fmt.Println("Unlock the lock: ", i)mutex.Unlock()c <- i}(i, channels[i])}time.Sleep(time.Second)fmt.Println("Unlock the main goroutine lock")mutex.Unlock()time.Sleep(time.Second)for _, c := range channels {<-c}}
// 程序输出Lock the lockThe lock is lockedNot lock: 1Not lock: 0Not lock: 2Not lock: 3Unlock the main goroutine lockLocked: 1Unlock the lock: 1Locked: 0Unlock the lock: 0Locked: 2Unlock the lock: 2Locked: 3Unlock the lock: 3
在解锁之前加锁会导致死锁
package mainimport ("fmt""sync")func main(){var mutex sync.Mutexmutex.Lock()fmt.Println("Locked")mutex.Lock()}
//程序输出Lockedfatal error: all goroutines are asleep - deadlock!
RWMutex(读写锁)
- RWMutex是单写多读锁,该锁可以加多个读锁或者一个写锁
- 读锁占用的情况下会阻止写,不会阻止读,多个goroutine可以同时获取读锁
- 写锁会阻止其他goroutine(无论读和写进来)进来,整个锁由该goroutine 独占
- 适用于读多写少的场景
写锁:Lock() 和 Unlock()
- Lock()加写锁,Unlock()解写锁
- 如果在加写锁之前已经由其他的读锁或者写锁,则Lock()会阻塞直到该锁可用。已经阻塞的Lock()调用会从获得的锁中排除新的读取器,即写锁的权限高于读锁,有写锁时优先进行写锁定
-
读锁:RLock() 和 RUnlock()
Rlock()加读锁,RUnlock()解读锁
- RLock()加读锁时,如果存在写锁,则无法加读锁;当只有读锁或者没有锁时,可以加读锁,读锁可以加载多个
- RUnlock()解读锁,Runlock()撤销单次RLock()调用,对于其他同时存在的读锁则没有效果
- 在没有读锁的情况下调用Runlock()会导致panic错误
下面展示一下我们的写锁:
// 在这里,我们使用一个type Handler struct {mx sync.RWMutexlastActive time.Time}func (h *Handler) Run() {h.mx.Lock(){h.lastActive = time.Now()}h.mx.Unlock()}
总结一下:互斥锁定就是单读单写,只能有一种状态;而读写锁则是可以用读锁和写锁,写锁的优先级别高于读锁。
