互斥锁:
互斥锁能保证在同一时刻,只有一个协程能够访问被保护的共享数据。
在 Golang 中,并发包 sync 里面的 Mutex 类型实现了互斥锁功能。它的核心是下面两个方法。
Lock 方法,用于加锁,当锁已经被占用时,调用协程会阻塞直到锁可用。
Unlock 方法,用于释放锁。
func (m *Mutex) Lock()
func (m *Mutex) Unlock()
import ("sync"
)type MutexCache struct {mu sync.Mutex // 互斥锁data map[string]string // 共享数据
}// NewMutexCache初始化一个MutexCache实例
func NewMutexCache() *MutexCache {c := &MutexCache{data: make(map[string]string)}return c
}// Set更新缓存
func (c *MutexCache) Set(key, value string) {c.mu.Lock()defer c.mu.Unlock()c.data[key] = value
}// Get从缓存中获取值
func (c *MutexCache) Get(key string) (string, bool) {c.mu.Lock()defer c.mu.Unlock()value, ok := c.data[key]return value, ok
}
读写锁:
为了提升共享数据读多写少场景的读性能,读写锁就能派上用场了。与互斥锁不同,读写锁区分了读操作和写操作。
读写锁有三种状态:
读模式锁定
写模式锁定
未锁定。
当读写锁处于读模式锁定时,可以有多个协程同时进行读操作;
而当读写锁处于写模式锁定时,只有一个协程能进行写操作,并且在写模式锁定时,读操作会被阻塞。
在GO语言中 sync 并发包的 RWMutex 类型实现了读写锁功能。
它的核心是下面几个方法。
Lock 方法,用于获取写锁,如果读锁或写锁已经被占用,则阻塞直到写锁可用。
Unlock 方法,用于释放写锁。
RLock 方法,用于获取读锁,当写锁没被占用时,可以获取到读锁,否则阻塞直到写锁被释放。
RUnlock 方法,用于释放读锁。
import ("sync"
)type RWMutexCache struct {rw sync.RWMutex // 读写锁data map[string]string // 共享数据
}// NewRWMutexCache初始化一个RWMutexCache实例
func NewRWMutexCache() *RWMutexCache {c := &RWMutexCache{data: make(map[string]string)}return c
}// Set更新缓存
func (c *RWMutexCache) Set(key, value string) {c.rw.Lock()defer c.rw.Unlock()c.data[key] = value
}// Get从缓存中获取值
func (c *RWMutexCache) Get(key string) (string, bool) {c.rw.RLock()defer c.rw.RUnlock()value, ok := c.data[key]return value, ok
}
分段锁:
对于 map 结构,当并发对 map 的访问相对均匀地分布在不同的键上时,可以用分段锁来提高并发性能。
无锁编程:
atomic 包提供了对数据进行原子操作的功能。所谓原子操作,是指对数据的一个操作,如果分成多个步骤,这些步骤对外会表现成不可分割的整体,要么完整地执行,要么就根本不执行,外界不会看到它们只执行到一半的状态。