面试官:说几个同步拿到异步操作结果的方式

最近工作中遇到个问题,感觉可以拿出来讲一下,因为这个问题的处理方式有很多种,涵盖的知识点也不少,我想如果我来作为一个面试官来问人的话,这个问题我是会拿出来作为考题的,先来看下一段代码

这段代码的意思很简单,在一个函数内会创建一个弹框,点击弹框上的按钮会改变变量name的值,随后将变量返回出去,但是众所周知弹框的点击事件是一个异步操作,而函数的return语句是会在弹框创建后同步执行的,所以这段代码无论弹框内怎么改变name的值,最终函数的执行结果永远都是一样的,也就是熊大,那么有没有办法将这段代码更改一下,让最终执行结果可以跟最初设计预期一样,只有当点击弹框按钮改变name值并且关闭弹框后,才将name值返回出去,也就是同步处理弹框的异步操作

CountDownLatch

说到同步,我们会想到许多方案,那么CountDownLatch肯定会是这些诸多方案中的其中一种,它允许线程去等待其他线程执行完成后再继续执行,执行的时机主要是靠里面的计数器,只有当计数器倒计时到0后,线程才可以继续执行

我们在代码中将CountDownLatch添加进去就变成了上面这样,return语句的上面多了一行latch.await(),这个时候计数器如果不倒计时到0,return语句是不会执行的,只有当点击了弹框上的按钮之后,计数器倒计时-1,这样才会执行return语句,这个时候,name就已经变成熊二了,但是事实真的如此吗?我们加个测试按钮执行一遍上述代码试试

代码执行后发现不但没有弹出框子,应用甚至还anr了,造成这个问题的原因主要还是因为CountDownLatch有阻塞线程的作用,在这里我们的线程自然就是主线程,所以肯定会阻塞的,但这是不是说明CountDownLatch不适合用在此类问题上呢?当然不是,我们将代码改一下

仅仅只是把需要阻塞的线程改成了非UI主线程,CountDownLatch的作用就能发挥出来了

所以我们得到的结论是CountDownLatch是可以处理一些异步操作同步处理的问题,但是要分场景,如果CountDownLatch需要阻塞的线程是主线程的话,就不推荐使用这个方案了

suspendCoroutine

言归正传,我们的框子还没出来呢,问题还没解决,既然CountDownLatch无法满足我们这个需求,我们需要想一想其他方案,在上面的例子中提到了几个关键点,阻塞,恢复,执行...脑海里面是不是立马就蹦出来协程俩字,阻塞也就相当于挂起,区别就是阻塞是阻的线程,而挂起挂的是协程,我们只需要将弹框放在一个挂起函数里面,然后点击完弹框按钮后将其恢复就可以了,这里我们使用suspendCoroutine函数来实现

使用suspendCoroutine的原因是我们需要用到它lambda表达式中的参数Continuation,因为这里的协程从挂起到恢复的时机就是点击弹框按钮,所以需要在点击按钮的时候手动触发Continuation.resume来恢复协程,否则协程将会一直处于挂起状态,resume里面的参数就是需要从协程内部返回给外界的值

这样就完成了使用同步的方式处理异步操作结果的效果了,当然除了suspendCoroutine之外,还有别的方式

Channel

协程中同步处理异步操作的方式有很多,刚才说的suspendCoroutine是基于协程的挂起恢复来实现,接下来说的Channel除了一部分挂起恢复的思想外,也基于生产者消费者的思想来实现

Channel相当于是一个消息管道,当调用了receive函数的时候,如果管道里面没有数据,协程会处于挂起状态,只有当生产者往里面添加数据的时候,receive函数才会恢复协程,并拿到生产者丢进来的数据,那么我们这里的生产者就是弹框,点击按钮就是生产数据,完整的代码如下

这段代码,消费者receive之后一直在等待生产者发送消息,这个时候协程是挂起的,随后在弹框中点击了按钮,调用了send函数之后,才在消息管道中添加了一条新的数据,receive函数收到数据后,协程恢复,数据被外界获得

CompletableDeferred

CompletableDeferred的用法与Channel很相似,但是它们也存在着一些差异

  • 通信方式CompletableDeferred是单向单次通信,Channel是双向持续通信
  • 数据量CompletableDeferred只能传递一个值,Channel由于是管道,所以可以传递多个值
  • 生命周期 :CompletableDeferred完成后立即失效,Channel可长期保持开放状态

上述例子使用CompletableDeferred实现的代码如下,代码很相似

总结

其实如果撇开同步这个因素,其实文中的例子用LiveData或者Flow也是可以实现同样的目的,关键还是遇到的场景以及问题必须得用同步的方式解决,文章写完了,希望对大家有帮助

相关推荐
小陈phd16 分钟前
langGraph从入门到精通(六)——基于 LangGraph 实现结构化输出与智能 Router 路由代理
android·网络·数据库
游戏开发爱好者842 分钟前
了解 Xcode 在 iOS 开发中的作用和功能有哪些
android·ios·小程序·https·uni-app·iphone·webview
_昨日重现2 小时前
Jetpack系列之Compose TopBar
android·android jetpack
林胖子的私生活2 小时前
绘制K线第五章:双指放大缩小
android
2501_937189232 小时前
IPTV 2026 优化版:解码适配 + 安全运维双升级,筑牢使用体验基石
android·源码·开源软件·源代码管理
朽木成才2 小时前
Android+Flutter混合开发实战
android·flutter
天燹3 小时前
Qt 6 嵌入 Android 原生应用完整教程
android·开发语言·qt
美狐美颜sdk3 小时前
企业级直播美颜SDK与动态贴纸SDK开发技术方案拆解与落地实践
android·人工智能·计算机视觉·第三方美颜sdk·人脸美型sdk
低调小一4 小时前
Kotlin 2025–2026 客户端开发路线:语言升级 × 跨端落地 × AI Agent 入门
开发语言·人工智能·kotlin
a努力。4 小时前
宇树Java面试被问:数据库死锁检测和自动回滚机制
java·数据库·elasticsearch·面试·职场和发展·rpc·jenkins