美文网首页
安卓面试总结——多线程

安卓面试总结——多线程

作者: Alex_ecb1 | 来源:发表于2019-12-10 21:54 被阅读0次

    1.handler机制

    使用:主线程创建Handler,复写handlemessage,子线程中sendmessage

    Handler被创建时,对类中有final类型的mlooper和mqueue的成员变量进行初始化,looper通过Looper.mylooper方法,调用threadlocal.get获取当前线程,这样就将handler和当前线程进行了绑定。然后mqueue就是looper的messagequeue,这样将messagequeue也进行了绑定。

    ps:Looper创建(prepare)的时候会在Threadlocal中若还没有looper,则会用set方法来保存唯一Looper,在set的时候会create一个threadlocalmap,key是currentthread对象,value是Looper的对象,以此将looper和threadLocal绑定。get的时候也是从map中去判断是否已经有Looper。

    Looper.loop 一个for的死循环,取queue的next进行msg.target.dipatchMesage(msg),若无callback,则handlemessage, msg在入列enqueueMessage的时候msg.target=this和handler绑定.

    loop为什么不会阻塞主线程,  linux的epoll机制, 当没有消息的时候会epoll.wait,等待句柄写的时候再唤醒,这个时候其实是阻塞的。2,所有的ui操作都通过handler来发消息操作。比如屏幕刷新16ms一个消息,你的各种点击事件,所以就会有句柄写操作,唤醒上文的wait操作,所以不会被卡死了。

    Epoll机制加事件驱动模型,就是UI线程的本质

    Messagequeue本质上就是massage的单链表,所以next方法就是msg的next. 使用message.obtain创建msg可以不用重复创建msg,减少jvm压力

    msg 延迟 处理时间以入列时间为准

    2 asynctask

    它适合网络请求或者简单数据处理。但不适合处理特别耗时的后台任务

     封装了线程池和handler的异步框架,用来执行异步任务,方便在工作线程和子线程切换

     onpreExcute - doInBackGround耗时操作 - onpostExcute

    在activity销毁时应该cancel asynctask,因为task仍持有上个acitity的引用,会导致结果丢失

    是串行synchronized的,可以并行,最多5个core线程,剩下的会排队 

    AsyncTask增加了一个新的接口executeOnExecutor,这个接口允许开发者提供自定义的线程池来运行和调度Thread

    AsyncTask的使用中会遇到三种泛型参数:Params, Progress 和 Result

    3 handlerThread

    继承了thread,内有looper对象,可以创建handler,可以在handleMessage中处理异步任务

    不阻塞UI线程,但是不能进行多任务处理,本质是一个串行队列

    4 intentService

    是service的抽象类,通过handler和handlerThread实现(内部开启异步线程,可以做耗时操作),通过handlerthread获取looper对象

    是处理异步请求 的类,,任务执行完成后会自动停止. 可以启动多次,每一个耗时操作都会以工作队列方式在IntentService的onHandleIntent

    回调方法中执行

    启动多次实例只有一个, 每次只执行一个工作线程, 顺序执行,执行完销毁,可以通过stopself方法销毁

    不建议使用bindservice方法启动,不然不会调用onhandleintent,那么就相当于启动service

    5 线程池 :

    入队顺序 核心线程->workingqueue->非i核心线程

         keepAliveTime:是非核心线程空闲时要等待下一个任务到来的时间

    ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。在只有入队高并发或出队高并发的情况下,因为操作数组,且不需要扩容,性能很高

    LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。有两把锁,在入队与出队都高并发的情况下,性能比ArrayBlockingQueue高很多

    PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。

    DelayQueue:一个使用优先级队列实现的无界阻塞队列。

    SynchronousQueue:一个不存储元素的阻塞队列。

    LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。

    FixedThreadPool 只有核心线程 执行长期的任务

    CachedThreadPool 只有非核心线程,等待时间60s SynchronousQueue队列,执行很多短期异步任务 

    SingleThreadPool单线程  一个任务一个任务执行

    NewScheduledThreadPool  周期性执行任务的场景

    6.多进程

    好处

    1、我们知道Android系统对每个应用进程的内存占用是有限制的,而且占用内存越大的进程,通常被系统杀死的可能性越大。让一个组件运行在单独的进程中,可以减少主进程所占用的内存,降低被系统杀死的概率。

    2、如果子进程因为某种原因崩溃了,不会直接导致主程序的崩溃,可以降低我们程序的崩溃率。

    3、即使主进程退出了,我们的子进程仍然可以继续工作,假设子进程是推送服务,在主进程退出的情况下,仍然能够保证用户可以收到推送消息。

    多进程引发的问题

    静态成员和单例模式失效

    线程同步机制失效

    SharedPreferences 可靠性降低

    Application 被多次创建

    AIDL:功能强大,支持进程间一对多的实时并发通信,并可实现 RPC (远程过程调用)。

    Messenger:支持一对多的串行实时通信, AIDL 的简化版本。

    Bundle:四大组件的进程通信方式,只能传输 Bundle 支持的数据类型。

    ContentProvider:强大的数据源访问支持,主要支持 CRUD 操作,一对多的进程间数据共享,例如我们的应用访问系统的通讯录数据。

    BroadcastReceiver:即广播,但只能单向通信,接收者只能被动的接收消息。

    文件共享:在非高并发情况下共享简单的数据。

    Socket:通过网络传输数据。

    linux多进程通信的方式

    1.管道:管道的一端连接一个进程的输出。这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。一个缓冲区不需要很大,它被设计成为环形的数据结构以便管道可以被循环利用。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。

    缺点: 在创建时分配一个管道时,缓存区大小比较有限;并不适合Android大量的进程通信

    2.消息队列:消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制。

    缺点: 信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;

    3.共享内存:什么是共享内存:顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;

    缺点: 通信需要设计复杂的机制保证各个进程通讯有效性。进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;安全问题比较突出,如果Android采用Binder 无异于将每个App放在一个内存中,这样是非常不安全的

    4.套接字:作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;

    5.信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

    6.信号: 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;接下来正面回答这个问题,从5个角度来展开对Binder的分析:

    (1) 从性能的角度 数据拷贝次数:Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,但共享内存方式一次内存拷贝都不需要;从性能角度看,Binder性能仅次于共享内存。

    (2) 从稳定性的角度:Binder是基于C/S架构的,Server端与Client端相对独立,稳定性较好;而共享内存实现方式复杂,没有客户与服务端之别, 需要充分考虑到访问临界资源的并发同步问题,否则可能会出现死锁等问题;从这稳定性角度看,Binder架构优越于共享内存。仅仅从以上两点,各有优劣,还不足以支撑google去采用binder的IPC机制,那么更重要的原因是:

    (3) 从安全的角度:传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份;而Android作为一个开放的开源体系,拥有非常多的开发平台,App来源甚广,因此手机的安全显得额外重要;对于普通用户,绝不希望从App商店下载偷窥隐射数据、后台造成手机耗电等等问题,传统Linux IPC无任何保护措施,完全由上层协议来确保。Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志,前面提到C/S架构,Android系统中对外只暴露Client端,Client端将任务发送给Server端,Server端会根据权限控制策略,判断UID/PID是否满足访问权限,目前权限控制很多时候是通过弹出权限询问对话框,让用户选择是否运行。

    相关文章

      网友评论

          本文标题:安卓面试总结——多线程

          本文链接:https://www.haomeiwen.com/subject/lkrngctx.html