最近看了一个Jetbrains大佬做的关于Kotlin协程和多并发的演讲, 这里稍微整理一下。
Kotlin协程的核心是挂起和恢复, 可以轻松处理高并发, 但是如果我门运行在协程中的函数是阻塞的, 协程的能力会被大大削弱。
举个例子:
在一个CoroutineScope中, 我们启动了两个协程, 让他们并发执行。
结果也是符合预期的。 515ms执行完毕。
@Test
fun coroutine_constructor_test2() = runBlocking {
val time = measureTimeMillis {
coroutineScope {
launch {
delay(500)
}
launch {
delay(500)
}
}
}
println("consumed time is $time")
}consumed time is 515如果我们把delay函数改成sleep, 让线程阻塞。
结果变成了1014ms。 原因就是这两个协程里的任务都是阻塞的。
@Test
fun coroutine_constructor_test2() = runBlocking {
val time = measureTimeMillis {
coroutineScope {
launch {
sleep(500)
}
launch {
sleep(500)
}
}
}
println("consumed time is $time")
}consumed time is 1014没关系, 我们可以设置一个Dispatcher, 让他们在两个线程中并发执行。 结果也是符合预期的, 很完美是不是?
@Test
fun coroutine_constructor_test2() = runBlocking {
val time = measureTimeMillis {
coroutineScope {
launch(Dispatchers.IO) {
println(Thread.currentThread().name)
sleep(500)
}
launch(Dispatchers.IO) {
println(Thread.currentThread().name)
sleep(500)
}
}
}
println("consumed time is $time")
}DefaultDispatcher-worker-1 @coroutine#2
DefaultDispatcher-worker-2 @coroutine#3
consumed time is 519但是如果有很多并发处理,我们启动100个协程, 让他们并行处理。 结果不是很理想, 足足慢了一倍。 原因就是我们不可能无限起线程。后来的任务只能等待前面的任务执行完毕。
@Test
fun coroutine_constructor_test2() = runBlocking {
val time = measureTimeMillis {
coroutineScope {
repeat(100) {
launch(Dispatchers.IO) {
println(Thread.currentThread().name)
sleep(500)
}
}
}
}
println("consumed time is $time")
}consumed time is 1057所以我们使用协程, 最理想的情况是每个方法都应该是非阻塞的, 例如OkHttp的设计。
我没看过OkHttp的源码, 直接上ChatGpt:
以下是 OkHttp 的工作原理:
连接池管理:OkHttp 使用连接池来管理与服务器的连接。连接池允许多个 HTTP 请求共享同一个 TCP 连接,从而减少了连接的建立和关闭的开销,并且提高了性能。
异步任务执行:OkHttp 使用异步任务执行的方式来发起 HTTP 请求和处理响应。当您发起一个 HTTP 请求时,OkHttp 不会立即阻塞当前线程,而是将请求添加到异步执行队列中,并且使用线程池来处理这些请求。
事件循环机制:OkHttp 使用了事件循环(event loop)机制来处理异步任务。它会在后台启动一个线程来轮询异步执行队列,一旦有请求完成或者有可读或可写的数据到达,它就会立即处理这些事件,并且触发相应的回调或者将数据返回给调用者。
总的来说,OkHttp 使用连接池和异步任务执行的方式来实现高效的 HTTP 请求处理。它并不是为每个请求启动一个独立的线程,而是通过复用连接和异步执行的方式来提高性能并减少资源的消耗。
有个OkHttp的事件循环的设计,Http请求可以异步执行。 Kotlin协程可以抹平异步程序的复杂度, 让代码看起来是同步的。 我认为这个是Kotlin协程的最大优势。