面试题答案
一键面试协程性能优化
- 协程创建策略
- 复用协程:避免频繁创建和销毁协程,使用
CoroutineScope
来管理协程生命周期,在需要复用的场景下,可在同一个CoroutineScope
中启动新的协程任务,而不是创建新的CoroutineScope
。例如,在Android开发中,ViewModel
就自带CoroutineScope
,在其生命周期内可复用此Scope
启动协程。 - 批量启动:对于多个相似的异步任务,可批量启动协程,而不是逐个启动。使用
launch
函数在CoroutineScope
中批量创建协程,通过join
函数等待所有协程执行完毕,如:
- 复用协程:避免频繁创建和销毁协程,使用
val scope = CoroutineScope(Job() + Dispatchers.Default)
val jobs = List(10) {
scope.launch {
// 异步任务逻辑
}
}
jobs.forEach { it.join() }
- 资源管理
- 及时释放资源:在协程执行完毕后,及时释放占用的资源,如文件句柄、数据库连接等。可以使用
try - finally
块,在finally
中关闭资源。例如:
- 及时释放资源:在协程执行完毕后,及时释放占用的资源,如文件句柄、数据库连接等。可以使用
val file = File("example.txt")
val inputStream = file.inputStream()
try {
// 协程中使用inputStream读取数据逻辑
} finally {
inputStream.close()
}
- **资源池**:对于一些创建开销较大的资源,如数据库连接,可使用资源池来管理。通过资源池获取和释放资源,减少资源创建和销毁的开销。
3. 线程池配置
- 合理选择调度器:Kotlin协程提供了不同的调度器,如Dispatchers.Default
用于CPU密集型任务,Dispatchers.IO
用于I/O密集型任务。根据任务类型选择合适的调度器,对于I/O操作,如网络请求、文件读写,使用Dispatchers.IO
,其内部使用了一个线程池来处理任务,避免阻塞主线程;对于CPU计算任务,使用Dispatchers.Default
。
- 调整线程池大小:根据系统资源和任务负载来调整线程池大小。对于I/O密集型任务,由于线程大部分时间处于等待I/O操作完成的状态,可适当增大线程池大小;对于CPU密集型任务,线程池大小应接近系统可用的CPU核心数,避免过多线程导致上下文切换开销增大。例如,在Dispatchers.IO
调度器中,可以通过Dispatchers.IO.limitedParallelism(n)
方法来设置线程池的最大并行度n
。
分布式环境下协程应用
- 处理网络请求
- 异步并发请求:使用协程可以方便地发起多个网络请求并异步处理结果。例如,使用
async
函数并发发起多个HTTP请求,然后通过await
函数获取结果。如下代码发起两个HTTP请求并获取响应:
- 异步并发请求:使用协程可以方便地发起多个网络请求并异步处理结果。例如,使用
val scope = CoroutineScope(Job() + Dispatchers.IO)
val deferred1 = scope.async {
// 发起第一个HTTP请求并返回响应
httpGet("http://example1.com")
}
val deferred2 = scope.async {
// 发起第二个HTTP请求并返回响应
httpGet("http://example2.com")
}
val response1 = deferred1.await()
val response2 = deferred2.await()
- **请求重试**:在网络请求失败时,利用协程的挂起特性实现重试机制。通过`repeat`函数或自定义重试逻辑,在失败时重新发起请求,如:
fun httpGet(url: String): String {
var retryCount = 0
while (true) {
try {
// 发起HTTP请求逻辑
return performHttpGet(url)
} catch (e: IOException) {
if (retryCount < 3) {
retryCount++
delay(1000) // 延迟1秒重试
} else {
throw e
}
}
}
}
- 数据一致性
- 分布式锁:使用协程结合分布式锁(如基于Redis的分布式锁)来保证数据一致性。在对共享数据进行读写操作前,获取分布式锁,操作完成后释放锁。例如,使用Redisson实现分布式锁:
val redissonClient: RedissonClient = Redisson.create()
val lock = redissonClient.getLock("myLock")
try {
lock.lock()
// 对共享数据进行读写操作逻辑
} finally {
lock.unlock()
}
- **事务处理**:在分布式环境下,可使用分布式事务框架(如Seata)结合协程来保证数据一致性。在协程中发起分布式事务,通过框架协调各个服务的操作,确保要么所有操作都成功,要么都失败回滚。例如,在Seata中,定义事务协调者和参与者,在协程中调用相关接口进行事务操作:
// 事务协调者
@GlobalTransactional
suspend fun globalTransaction() {
// 调用参与者服务逻辑
participantService1.doSomething()
participantService2.doSomethingElse()
}
// 参与者服务
class ParticipantService1 {
@Transactional
suspend fun doSomething() {
// 本地数据库操作逻辑
}
}