面试题答案
一键面试方法一:使用sync.Mutex
- 实现方式:
- 定义一个结构体,包含一个原生的
map
和一个sync.Mutex
。 - 在对
map
进行读写操作前,先获取锁,操作完成后释放锁。
type SafeMap struct { mu sync.Mutex data map[string]interface{} } func (sm *SafeMap) Set(key string, value interface{}) { sm.mu.Lock() defer sm.mu.Unlock() if sm.data == nil { sm.data = make(map[string]interface{}) } sm.data[key] = value } func (sm *SafeMap) Get(key string) (interface{}, bool) { sm.mu.Lock() defer sm.mu.Unlock() if sm.data == nil { return nil, false } value, exists := sm.data[key] return value, exists }
- 定义一个结构体,包含一个原生的
- 优点:
- 实现简单直接,易于理解和维护。
- 对于一般性的并发读写场景能很好地保证数据一致性。
- 缺点:
- 性能瓶颈明显,因为每次读写都需要获取锁,在高并发场景下,锁竞争会导致性能下降。
- 粒度较粗,整个
map
被一个锁保护,即使不同的键值对操作也会相互影响。
方法二:使用sync.RWMutex
- 实现方式:
- 同样定义一个结构体,包含
map
和sync.RWMutex
。读操作使用读锁,写操作使用写锁。
type SafeMapWithRWMutex struct { mu sync.RWMutex data map[string]interface{} } func (sm *SafeMapWithRWMutex) Set(key string, value interface{}) { sm.mu.Lock() defer sm.mu.Unlock() if sm.data == nil { sm.data = make(map[string]interface{}) } sm.data[key] = value } func (sm *SafeMapWithRWMutex) Get(key string) (interface{}, bool) { sm.mu.RLock() defer sm.mu.RUnlock() if sm.data == nil { return nil, false } value, exists := sm.data[key] return value, exists }
- 同样定义一个结构体,包含
- 优点:
- 在读多写少的场景下性能较好,因为读操作可以并发进行,只要没有写操作。
- 相比于
sync.Mutex
,能在一定程度上提高并发性能。
- 缺点:
- 写操作仍然需要独占锁,在写操作频繁时性能提升有限。
- 实现比单纯使用
sync.Mutex
稍复杂,需要注意读写锁的正确使用。
方法三:使用sync.Map
- 实现方式:
- Go 1.9 引入了
sync.Map
,它是一个线程安全的map
。使用方式与原生map
类似,但不需要手动加锁。
var syncMap sync.Map syncMap.Store("key", "value") value, exists := syncMap.Load("key")
- Go 1.9 引入了
- 优点:
- 原生支持并发安全,使用简单,不需要开发者手动管理锁。
- 性能较好,在高并发场景下表现优秀,采用了更复杂的内部结构来减少锁竞争。
- 缺点:
- 不支持遍历操作(虽然可以通过
Range
方法实现类似遍历功能,但与原生map
的遍历行为有差异)。 - 相比原生
map
,内存占用可能稍高,因为内部实现更复杂。
- 不支持遍历操作(虽然可以通过