Android面试集锦系列(41)——线程同步
前言
最近领导让我在插件框架上加上一个接口,在宿主应用中可以调用所有的插件去清除自己的缓存数据,当完成所有插件的清除任务后再执行下一步操作。领导就是需求嘛,领导改变注意那是再正常不过的事了,但是不是领导的需求人员有可能变多了会有人身危险。我平时和同事吹牛时,常和他们说工作的七字真言:
“不急、不怕、不要脸”(抄自冯唐)
我认为对于软件开发来说,这句话很值得品味。在需求改变时,不要急于修改代码,而是要先做一个全盘的考虑,有些时候你还没考虑好,需求方就说不要做了。在接到新任务或者遇到困难时不要怕,没什么可怕的,你不难受你就不会有提高。最后,不要脸更是程序员最需要的,这个自己体会。
面试题:如何处理线程同步的问题?
有可能很多人对插件并不了解,不过没关系,这个需求简单地说就是主线程要等待多个子线程全部完成工作后,才能继续执行。
说到多线程的同步问题,面试多的人应该很容易被面试官问:Object的wait和notify/notifyAll如何实现线程同步?
在Object.java中,定义了wait(), notify()和notifyAll()等接口。wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。而notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。
wait和yield(或sleep)的区别?
- wait()是让线程由“运行状态”进入到“等待(阻塞)状态”,而yield()是让线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权;但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权。
- wait()是会线程释放它所持有对象的同步锁,而yield()方法不会释放锁。
而我接触到的很多情况是:问线程同步的问题,大多数人基本上只知道synchronized。
要搞清线程的同步问题,大家要先了解一下“对象的同步锁”,这个留给大家自己去看吧,这里不做展开。我们回到新接到的这个需求上来,这个场景其实挺合适做为一个面试题的。
如何实现呢?我想到一个简单的方法就是用CountDownLatch。
CountDownLatch:一个同步辅助类(大名鼎鼎的java.util.concurrent包),在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
用给定的任务数初始化CountDownLatch,一个线程工作完成(任务成功或者失败都算工作完成)就调用 countDown() 方法,当计数到达零之前,await 方法会一直受阻塞。当计数器为零时,会释放所有等待的线程,await后的代码将被执行。
CountDownLatch计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。
还有其他的实现方式吗?这个是肯定的。比如,真接上Thread.jion,代码难看是会难看点,但也能完成这个需求。
我还查到一种方式是使用java.util.concurrent.ExecutorService的awaitTermination阻塞主线程,等待线程池的所有线程执行完成。需要设置一个超时时间的参数,如果超时则awaitTermination返回false,如果线程池中的线程全部执行完成,返回true。
小结
因为现在有很多开源的框架或者代码库,帮我们解决了很多底层诸如网络请求、线程池管理的问题,使得很多情况下我们都不怎么接触到线程同步的问题。不过还有很有必要抽时间来学习一些线程同步的知识,对我们提高并发编程的能力很有帮助。
如果大家有更好的方式实现我开头提到的需求,可以回帖一起讨论一下。
最后
在现在这个金三银四的面试季,我自己在网上也搜集了很多资料做成了文档和架构视频资料免费分享给大家【包括高级UI、性能优化、架构师课程、NDK、Kotlin、混合式开发(ReactNative+Weex)、Flutter等架构技术资料】,希望能帮助到您面试前的复习且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。