面试题答案
一键面试1. 合理控制锁粒度优化性能示例
假设我们有一个简单的银行账户结构体,包含余额和操作方法。
package main
import (
"fmt"
"sync"
)
// 银行账户结构体
type BankAccount struct {
balance int
mu sync.Mutex
}
// 存款方法
func (b *BankAccount) Deposit(amount int) {
b.mu.Lock()
b.balance += amount
b.mu.Unlock()
}
// 取款方法
func (b *BankAccount) Withdraw(amount int) {
b.mu.Lock()
if b.balance >= amount {
b.balance -= amount
}
b.mu.Unlock()
}
// 获取余额方法
func (b *BankAccount) GetBalance() int {
b.mu.Lock()
defer b.mu.Unlock()
return b.balance
}
在上述例子中,锁粒度较粗,所有对 balance
的操作都在同一个锁保护下。如果我们有多个不同的操作,且这些操作相互独立,就可以将锁粒度细化。
package main
import (
"fmt"
"sync"
)
// 银行账户结构体
type BankAccount struct {
balance int
depositMu sync.Mutex
withdrawMu sync.Mutex
getBalanceMu sync.Mutex
}
// 存款方法
func (b *BankAccount) Deposit(amount int) {
b.depositMu.Lock()
b.balance += amount
b.depositMu.Unlock()
}
// 取款方法
func (b *BankAccount) Withdraw(amount int) {
b.withdrawMu.Lock()
if b.balance >= amount {
b.balance -= amount
}
b.withdrawMu.Unlock()
}
// 获取余额方法
func (b *BankAccount) GetBalance() int {
b.getBalanceMu.Lock()
defer b.getBalanceMu.Unlock()
return b.balance
}
这样,不同的操作由不同的锁保护,提高了并发性能。
2. 粗粒度锁优缺点
- 优点:
- 实现简单:代码逻辑相对简单,只需要一个锁来保护共享资源,容易理解和维护。
- 数据一致性强:所有对共享资源的操作都在同一个锁的保护下,能很好地保证数据的一致性。
- 缺点:
- 并发性能低:如果有多个并发操作,每次只能有一个操作获得锁,其他操作需要等待,可能导致线程长时间阻塞,降低系统的并发处理能力。
- 锁争用严重:当并发访问量高时,多个线程频繁竞争同一个锁,会增加锁的开销,降低系统性能。
3. 细粒度锁优缺点
- 优点:
- 并发性能高:不同的操作可以由不同的锁保护,只要操作对应的锁不冲突,就可以并发执行,大大提高了系统的并发处理能力。
- 锁争用减少:由于锁的粒度变小,多个线程竞争同一个锁的概率降低,减少了锁争用带来的开销。
- 缺点:
- 实现复杂:需要更多的锁来保护不同的资源或操作,增加了代码的复杂度,可能导致死锁等问题,对开发者的要求更高。
- 数据一致性维护困难:多个锁可能导致在不同锁之间的数据一致性问题,需要更小心地处理跨锁操作,以保证数据的一致性。