Android多线程面试---谈谈你对多线程的理解+handle机制

要谈线程,就不得不引出进程的概念,在Android中一个应用程序就是一个单独的进程,一般来说,当我们运行一个应用,系统就会自动创建一个进程,并且为这个进程创建一个主线程--UI线程,这样就可以运行MainActivity,应用程序的组件默认都是运行在这个进程中,当然我们可以指定Android四大组件的运行进程,在清单文件中对应的组件通过设置属性---android:process来使组件运行在不同的进程中,Android的多进程有利也有弊:

  1. 优势:将应用的组件拆分在不同的进程中,可以使主线程得到更多的内存容量,因为Android系统为每个应用程序进程分配的内存是有限量的,应用程序的进程使用内存超过了这个容量,就会报 oom 也就是内存泄漏,这是致命的bug
  2. 坏处:每个进程都有自己的虚拟机实例,将一个应用程序分置在不同的进程中,会使数据在进程间的通讯变得很麻烦,当然我们可以通过进程间的通讯来解决这个问题,进程间通讯方法:AIDL、文件(磁盘)、Binder、Messenger、ContentProvider(内容提供者)、Socket,因为这篇文章是讲线程,所以进程以后再说,

上述就是Android中进程的一些基本知识点,有了进程的概念,就可以谈线程,线程是操作系统能够进行运算调度的最小单位,线程是进程的子集,线程可以并行的执行不同任务,所有的线程共享同一片内存空间,这就为线程间通信提供了基础,线程有五种状态:创建,就绪,运行,阻塞,死亡;

有些面试会问到,线程的start()和run()是什么区别?start是启动一个线程,run是让这个线程运行,很明显就是start在创建一个新的线程,而run是操作当前这个线程去运行。

当应用程序,也就是进程中有组件在运行的时候,应用程序的主线程UI线程肯定是在运行状态的,默认情况下,应用的所有组件都是在UI线程完成的,包括响应用户的操作,组件的生命周期方法的调用,UI更新等等,因此在UI线程中执行耗时操作后,线程阻塞超过5秒,程序就会处于ANR的状态,application not response,这是绝对不能出现的bug,所以我们需要在工作线程中做各种耗时操作,但是更新UI的操作一定要在UI线程中,这是因为更新UI是通过调用invalidate()方法重绘界面来实现的,而invalidate()方法是非线程安全的,如果我们在一个线程更新了UI,有可能其他线程也在更新UI,这时候就出现了显示不同步的数据错乱!!

在Android中,我们将除了UI线程的其他所有线程都称为工作线程,也就是说Android中只有两种线程:UI线程、工作线程,所以我们要在工作线程做耗时操作(读写数据库,文件,访问网络请求等),而在UI线程刷新页面,这就要求我们掌握线程间的通讯,Android系统提供的线程通讯有两种:AsyncTask机制、Handler机制

AsyncTask机制:

AsyncTask是一个异步任务,在UI线程运行的时候,可以在后台执行一些异步的操作,在不使用工作线程或者Handler的时候,它在后台执行将结果返回给UI线程,AsyncTask的优点是代码逻辑简洁易读易维护,但是它不能去运行耗时特别长的任务,适合执行几秒内结束的操作,长时间执行的任务应该交给工作线程或者是Handler机制去执行,而且一次执行后就结束了,没有提供延时、循环的任务,那么AsyncTask使用是怎么一回事?

AsyncTask只能通过继承来使用,也就是新建一个类去继承AsyncTask,大概重写它的四个方法就够用了:

  1. 任务执行前                      ---onProExecute(),比如执行正在下载的窗口,UI线程
  2. 开启工作线程,执行任务---doInBackGroud(),后台执行,工作线程
  3. 实时返回进度条的数据    ---onProgressUpdate(),更新后台任务的进度,UI线程
  4. 任务执行完成                  ---onPostExecute(),返回任务结果,UI线程

在使用AsyncTask时,需要注意几个点:

  1. AsyncTask必须在UI线程中加载
  2. 必须在UI线程中实例化
  3. 不要手动去调用这些方法,交给系统
  4. 启动执行AsyncTask任务的execute方法,必须在UI线程调用
  5. AsyncTask任务只能执行一次

Handler机制:

Handler机制,也就是通过Handler,Looper,MessageQueue和Message这几个类协调来完成,首先看看Handler机制实现线程通信的原理,先说说这几个类的作用:

  1. handler:发送和接收处理Message对象和Runnable对象
  2. Looper:用MessageQueue中取出Message对象,通过匹配Taget发给Handler处理
  3. MessageQueue:消息队列,用于存放通过Handler发布的消息
  4. Message:消息的类型,包含一些实例对象

一个Handler对象仅与一个Looper相关联,一个Message也仅与一个目标Handler对象相关联,一个Looper对象拥有一个MessageQueue,多个不同的Handler对象可以与同一个Message相关联,例如UI线程中创建的Handler对象发消息时都会发送到同一个Looper中,也会放到同一个MessageQueue里边,这就是Handler机制实现多线程通信的核心。

刚刚说一个Handler对象仅与一个Looper相关联,那是在什么时候去关联的呢?是在创建Handler对象的时候,UI线程在创建的时候就会拥有自己的一个handler对象和一个Looper对象(工作线程在创建Handler对象的时候,首先要调用Looper.prepare()来生成工作线程对应的Looper,不然会抛异常),然后在UI主线程中的任何地方创建的handler都会默认的指向UI主线程的Looper对象,和这个Looper对象相关联,然后我们将Handler对象传递到任意需要的工作线程中,这时候工作线程所使用的Handler对象处理的消息就是在UI主线程的Looper对象中,也就是在UI主线程的MessageQueue中,从而达到线程间的通信。

实质上,Android的多线程就是将不同的Message放到同一个MessageQueue,也就是说多个线程间共享一个MessageQueue,然后UI主线程或者工作线程等待MessageQueue取出Message,交还给不同线程下的Handler去处理消息

我们再从源码层面分析一下handler,先整理几个问题?

  1. Looper类是用来存放MessageQueue,然后控制msg的入队和出队,

 

IntentService,与普通service不同的是在oncreate时开启一个线程

 

线程同步的机制:

  • synchronize方法或者同步代码块都可以
  • 在子线程run方法里加lock(),最后finally的时候unlock(),最好是在循环体之前加锁

 

多线程通信:

  • Handler
  • 广播
  • EventBus
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章