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

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

这段代码的意思很简单,在一个函数内会创建一个弹框,点击弹框上的按钮会改变变量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也是可以实现同样的目的,关键还是遇到的场景以及问题必须得用同步的方式解决,文章写完了,希望对大家有帮助

相关推荐
2501_9151063219 分钟前
iOS WebView 调试实战,第三方脚本加载失败与内容安全策略冲突问题排查指南
android·ios·小程序·https·uni-app·iphone·webview
消失的旧时光-19431 小时前
Android 键盘
android·键盘监听
小悟空13 小时前
[AI 生成] Flink 面试题
大数据·面试·flink
Jackilina_Stone14 小时前
【faiss】用于高效相似性搜索和聚类的C++库 | 源码详解与编译安装
android·linux·c++·编译·faiss
Sherry00715 小时前
CSS Grid 交互式指南(译)(下)
css·面试
一只毛驴15 小时前
浏览器中的事件冒泡,事件捕获,事件委托
前端·面试
一只叫煤球的猫15 小时前
你真的处理好 null 了吗?——11种常见但容易被忽视的空值处理方式
java·后端·面试
棒棒AIT15 小时前
mac 苹果电脑 Intel 芯片(Mac X86) 安卓虚拟机 Android模拟器 的救命稻草(下载安装指南)
android·游戏·macos·安卓·mac
KarrySmile15 小时前
Day04–链表–24. 两两交换链表中的节点,19. 删除链表的倒数第 N 个结点,面试题 02.07. 链表相交,142. 环形链表 II
算法·链表·面试·双指针法·虚拟头结点·环形链表
fishwheel15 小时前
Android:Reverse 实战 part 2 番外 IDA python
android·python·安全