一、前言
go語言類似Java JUC包也提供了一些列用於多線程之間進行同步的措施,比如低級的同步措施有 鎖、CAS、原子變量操作類。相比Java來說go提供了獨特的基於通道的同步措施。本節我們先來看看go中讀寫鎖
二、讀寫鎖
go中讀寫鎖,在沒有線程獲取寫鎖情況下多個線程可以同時獲取讀鎖,讀鎖是可重入鎖,寫鎖則是互斥鎖(不可重入)。
package main
import (
"fmt"
"sync"
"time"
)
var (
counter int //計數器
wg sync.WaitGroup //信號量
lock sync.RWMutex //讀寫鎖
)
func main() {
//1.兩個信號
wg.Add(1)
//2.獲取讀鎖
fmt.Println("main thread wait rlock")
lock.RLock()
fmt.Println("main thread got rlock")
//3.開啓子線程
go incCounter()
fmt.Println(counter)
time.Sleep(time.Second * 5)
//4.釋放讀鎖
lock.RUnlock()
fmt.Println("main thread release rlock")
//5.等待子線程結束
wg.Wait()
fmt.Println(counter)
}
func incCounter() {
defer wg.Done()
//2.1.獲取鎖
fmt.Println("sub thread wait rlock")
lock.Lock()
fmt.Println("sub thread got rlock")
//2.2.計數加1
counter++
//2.3.釋放獨佔鎖
lock.Unlock()
fmt.Println("sub thread relese rlock")
}
- 如上代碼go中使用sync.RWMutex可以獲取一個開箱即用的讀寫鎖
- 代碼(2)主線程使用lock.RLock()獲取讀鎖,然後開啓了子線程(代碼3),然後在主線程持有讀鎖的情況下休眠了5s後釋放了鎖。
- 子線程在代碼2.1嘗試使用 lock.Lock()獲取寫鎖,由於主線程還沒釋放讀鎖,所以子線程阻塞到了這裏,直到主線程休眠後執行代碼4釋放了讀鎖。
- 執行代碼會輸出:
main thread wait rlock
main thread got rlock
0
sub thread wait rlock
main thread release rlock
sub thread got rlock
sub thread relese rlock
1
這個例子說明了,當有線程獲取了讀鎖並沒釋放時候,獲取寫鎖的線程要等待。
另外使用下面方法可以驗證讀鎖是可重入鎖:
lock.RLock()
lock.RLock()
fmt.Println(counter)
lock.RUnlock()
lock.RUnlock()
使用下面代碼可以驗證同一個線程的讀鎖,不能晉升爲寫鎖:
lock.RLock()
lock.Lock()
fmt.Println(counter)
lock.Unlock()
lock.RUnlock()
上面代碼執行會報錯:
三、總結
go中讀寫鎖中的讀鎖是可重入共享鎖,寫鎖是互斥鎖,讀鎖不能晉升爲寫鎖。