面试题答案
一键面试Mutex底层实现原理
- 锁的状态机
- Go语言的
Mutex
有两种状态:normal
和starving
。 normal
状态下,新的goroutine可以尝试获取锁。如果锁可用,它可以直接获取锁。如果锁不可用,它会进入等待队列。starving
状态下,新的goroutine不会尝试获取锁,而是直接加入到等待队列的尾部。当一个goroutine在等待队列中等待超过1ms,锁会进入starving
状态。
- Go语言的
- 调度算法
- 在
normal
状态下,当一个goroutine释放锁时,等待队列中的第一个goroutine会被唤醒并获取锁。如果此时有新的goroutine同时尝试获取锁,新的goroutine有更高的机会获取锁,这可能导致等待队列中的goroutine饥饿。 - 在
starving
状态下,当一个goroutine释放锁时,等待队列中最久的goroutine会获取锁,从而避免饥饿。如果一个被唤醒的goroutine获取锁后,发现没有其他等待的goroutine或者等待时间都小于1ms,锁会回到normal
状态。
- 在
RWMutex底层实现原理
- 锁的状态机
RWMutex
有读锁和写锁。写锁是独占的,读锁可以被多个goroutine同时持有。- 它有一个状态变量,通过不同的位来表示写锁是否被持有,以及有多少个读锁被持有。
- 调度算法
- 当写锁被持有时,任何读锁和写锁的获取请求都会被阻塞。
- 当读锁被持有时,写锁的获取请求会被阻塞,直到所有读锁都被释放。
- 读锁的获取可以并行进行,只要没有写锁被持有。
应用陷阱及避免方法
- Mutex相关陷阱
- 饥饿问题:在
normal
状态下,新的goroutine可能频繁获取锁,导致等待队列中的goroutine饥饿。- 避免方法:如果担心饥饿问题,可以尽量减少锁的持有时间,或者在适当的时候手动切换锁到
starving
状态(虽然Go语言本身会自动处理,但在一些极端情况下可以考虑手动干预)。
- 避免方法:如果担心饥饿问题,可以尽量减少锁的持有时间,或者在适当的时候手动切换锁到
- 死锁问题:如果一个goroutine在持有锁的情况下再次尝试获取同一把锁,会导致死锁。
- 避免方法:仔细检查代码逻辑,确保不会出现嵌套获取同一把锁的情况。可以使用工具如
go vet
来检测潜在的死锁。
- 避免方法:仔细检查代码逻辑,确保不会出现嵌套获取同一把锁的情况。可以使用工具如
- 饥饿问题:在
- RWMutex相关陷阱
- 写锁饥饿:如果读操作非常频繁,写操作可能长时间无法获取锁,导致写饥饿。
- 避免方法:可以限制读操作的并发数量,或者设置一个写操作优先的机制,例如当写操作等待时间超过一定阈值时,优先处理写操作。
- 数据一致性问题:在读多写少的场景中,如果在读取数据时没有正确加读锁,可能读取到脏数据。
- 避免方法:在读取共享数据时,一定要正确获取读锁,在写入共享数据时,正确获取写锁。
- 写锁饥饿:如果读操作非常频繁,写操作可能长时间无法获取锁,导致写饥饿。