面试题答案
一键面试可能出现问题的场景分析
- 性能瓶颈
- 协程创建开销:在高并发场景下,如果频繁创建大量协程,每次创建协程的初始化开销(如分配栈空间等)会导致性能瓶颈。例如,在一个短时间内需要处理大量HTTP请求的Web应用中,为每个请求创建新的协程处理,可能导致性能问题。
- 协程调度开销:当协程数量过多,协程调度器在不同协程间切换上下文的开销会增大。比如,在一个包含大量I/O操作协程和CPU密集型协程混合的系统中,调度器频繁在它们之间切换,会消耗过多资源。
- 资源竞争
- 共享资源访问:多个协程同时访问和修改共享资源(如内存中的共享数据结构、文件等)时会出现资源竞争。例如,多个协程同时对一个共享的计数器进行累加操作,如果没有适当的同步机制,结果可能不正确。
- 外部资源竞争:对于外部资源(如数据库连接池、网络连接等),多个协程竞争获取资源可能导致等待时间过长,甚至死锁。比如,多个协程同时请求数据库连接,而连接池资源有限,可能会使部分协程长时间等待。
优化方法
- 优化协程调度器
- 使用合适的调度器:根据任务类型选择调度器。对于CPU密集型任务,使用
Dispatchers.Default
,它使用固定大小的线程池,能有效避免过多线程竞争CPU资源;对于I/O密集型任务,使用Dispatchers.IO
,它有一个较大的线程池,适合处理I/O操作。例如:
- 使用合适的调度器:根据任务类型选择调度器。对于CPU密集型任务,使用
// CPU密集型任务
GlobalScope.launch(Dispatchers.Default) {
// 执行CPU密集型计算
}
// I/O密集型任务
GlobalScope.launch(Dispatchers.IO) {
// 执行I/O操作,如文件读取或网络请求
}
- **自定义调度器**:如果默认调度器不能满足需求,可以自定义调度器。例如,创建一个特定大小线程池的调度器,以适应特定场景下的并发需求。
val customDispatcher = Executors.newFixedThreadPool(10).asCoroutineDispatcher()
GlobalScope.launch(customDispatcher) {
// 任务执行
}
- 资源管理
- 共享资源同步:使用
synchronized
关键字、Mutex
等机制来同步对共享资源的访问。例如,对于共享计数器的操作:
- 共享资源同步:使用
val mutex = Mutex()
var counter = 0
GlobalScope.launch {
mutex.lock()
try {
counter++
} finally {
mutex.unlock()
}
}
- **资源池管理**:对于外部资源,如数据库连接池,合理配置连接池大小,避免资源浪费和竞争。可以使用第三方库如HikariCP来管理数据库连接池。
3. 并发控制
- 限制并发数量:使用Semaphore
来限制同时执行的协程数量。例如,假设系统最多能处理10个并发任务:
val semaphore = Semaphore(10)
for (i in 0 until 100) {
GlobalScope.launch {
semaphore.acquire()
try {
// 执行任务
} finally {
semaphore.release()
}
}
}
- **协程超时控制**:为协程设置超时时间,避免协程长时间占用资源。例如:
val result = withTimeoutOrNull(1000) { // 1000毫秒超时
// 执行任务
}
if (result == null) {
// 处理超时情况
}
代码对比及性能差异
优化前代码:
import kotlinx.coroutines.*
fun main() = runBlocking {
val jobs = mutableListOf<Job>()
for (i in 0 until 1000) {
jobs.add(GlobalScope.launch {
// 模拟一些计算或I/O操作
delay(100)
})
}
jobs.forEach { it.join() }
}
此代码在高并发时会面临性能问题,因为大量协程创建和调度带来较大开销,且没有资源管理和并发控制。
优化后代码:
import kotlinx.coroutines.*
fun main() = runBlocking {
val semaphore = Semaphore(50)
val jobs = mutableListOf<Job>()
for (i in 0 until 1000) {
jobs.add(GlobalScope.launch(Dispatchers.IO) {
semaphore.acquire()
try {
withTimeoutOrNull(200) {
// 模拟一些计算或I/O操作
delay(100)
}
} finally {
semaphore.release()
}
})
}
jobs.forEach { it.join() }
}
性能差异:优化前由于协程创建和调度开销大,系统可能变得卡顿甚至崩溃。优化后,通过限制并发数量(Semaphore
)、使用合适的调度器(Dispatchers.IO
)以及设置超时控制,系统能更稳定地处理高并发任务,减少资源竞争和性能瓶颈,提高整体性能和稳定性。例如,在实际测试中,优化前处理1000个任务可能需要几分钟甚至更长时间,而优化后可能在几十秒内完成,且系统资源利用率更合理。