- 线程分为主线程和子线程。主线程是指进程所拥有的线程(java 默认情况下一个进程只有一个线程,这个线程就是主线程),主要处理和界面相关的事情,子线程执行耗时操作,子线程也叫工作线程。
AsyncThread
封装了线程池和 Handler,主要是为了方便开发者在子线程中更新UI。HandlerThread
是一种具有消息循环的线程,内部可以使用 Handler。IntentService
是一个服务,颞部采用 HandlerThread 执行任务,当任务执行完毕后 IntentService 会自动退出。由于是四大组件之一,优先级比较高,不容易被后台杀死,保证任务的执行。- 操作系统中,线程是操作系统调度的最小单元,同时线程又是一种受限的系统资源。线程不可能无限制产生,并且线程的创建和销毁都会有相应的开销。当系统中存在大量的线程,系统通过时间片轮转的方式调度每个线程,因此线程不可能做到绝对的并行,除非线程的数量小于CPU的数量,一般来说不可能。
11.2 Android的线程形态
AsyncTask
不适合执行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池。因为如果特别耗时的话,可能 Activity 都结束了,影响到 AsyncTask 的执行。AsyncTask
的类必须在主线程中加载,这就意味着第一次访问 AsyncTask 必须发生在主线程。AsyncTask
的对象必须在主线程中创建,execute 方法必须在UI线程调用。- 不要在程序中直接调用
onPreExecute、onPostExecute、onProgressUpdate、doInBackground
方法 - 一个
AsyncTask
对象只能执行一次,即只能调用一次execute
方法,否则会报运行时异常。
工作原理:
1: 首先系统会把AsyncTask
的 Params 参数封装成FutureTask
对象,FutureTask
是一个并发类,这里充当了 Runnable 的作用。
2: 接着FutureTask
就会交给SerialExecutor
的 execute 方法去处理,SerialExecutor
的 execute 方法首先把FutureTask
插入到任务队列 mTasks 中去,如果此时没有正在活动的 AsyncTask 任务,那么就会调用SerialExecutor
的scheduleNext
方法通过线程池THREAD_POOL_EXECUTOR
来执行下一个AsyncTask
任务。同时当一个AsyncTask
任务执行完后,就会继续执行其他任务知道所有的任务都被执行为止(默认情况下,AsyncTask 是串行执行的)
3: 执行任务后,通过InternalHandler
将执行环境切换到主线程。
介绍
SerialExecutor
:用于任务的排队。
THREAD_POOL_EXECUTOR
:用于真正地执行任务。
InternalHandler
:用于将执行环境从线程池切换到主线程。
11.3 HandlerThread
实现:在 run 方法中通过Looper.prepare()
来创建消息队列,并通过Looper.loop()
来开启消息循环,这样就允许在HandlerThread 中创建 Handler 了。
使用场景:IntentService
。由于HandlerThread 的 run 方法是一个无限循环,因此当明确不再使用 HandlerThread 的时候,可以通过 quit 或者 quitSafely 来终止线程的执行。
11.4 IntentService
IntentService
是一个抽象类,继承了 Service。IntentService 可以执行后台耗时的任务,当任务执行后他会自动停止,同时由于 IntentService 是服务的原因,导致优先级比单纯的线程高很多,不容易被系统杀死。
IntentService
被第一次启动时,他的 onCreate 方法会被调用。
public void onCreate() {
super.onCreate();
/**
* 创建一个HandlerThread对象,HandlerThread是Thread,不是Handler
*/
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
/**
* 获取在HandlerThread的run方法中创建的Looper对象,当有消息的时候,就会调用Handler的dispatchMessage方法分发消息给特定的方法执行
*
* 在该类,消息队列一有消息,Looper就会调用ServiceHandler的dispatchMessage方法,
* 然后调用handleMessage(),而这个过程就是在HandlerThread的run()中执行的(因为Looper.looper会调用Handler的dispatchMessage方法)
*/
mServiceLooper = thread.getLooper();
/**
* 根据HandlerThread对象中的Looper对象创建ServiceHandler对象
*
* ServiceHandler继承了handler
* mServiceHandler发送的消息在HandlerThread中执行(因为用的是HandlerThread中的Looper)
*/
mServiceHandler = new ServiceHandler(mServiceLooper);
}
每次启动 IntentService
,onStartCommand
会被调用,onStartCommand
调用onstart
。
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
原理
IntentService
通过 ServiceHandler
发送一个消息到消息队列,而消息队列是在HandlerThread
中创建的,在 HandlerThread
中由于消息循环,Looper.loop()
将消息取出来,并调用ServiceHandler
的dispatchMessage
方法,然后调用IntentService
的handleMessage
方法,在该方法中调用了onHandleIntent
方法和stopSelf(int startId)方法,这样startService
中的intent就到了这里。onHandleIntent
是一个抽象方法,需要我们在子类中实现,并能通过 intent 区别任务。如果当前只存在一个后台任务,那么onHandleIntent
方法执行完这个任务之后,stopSelf(int startId)
就会直接停止服务;如果目前存在多个后台任务,那么当onHandleIntent
执行完最后一个任务时,stopSelf(int startId)
才会直接停止服务。
由于每执行一个后台任务就必须启动一次IntentService
,而IntentService
内部则通过消息的方式向HandlerThread
请求执行任务,Handler 中的 Looper 是顺序处理消息的,这就意味着 IntentService 也是顺序执行后台任务的,当有多个任务同时存在,这些任务会按照外界发起的顺序排队执行。
stopSelf() 和 stopSelf(int startId)
stopSelf()
:会立刻停止服务,此时可能还有其他消息未处理。
stopSelf(int startId)
:在尝试停止服务之前会判断最近启动服务的次数是否和 startId 相等,如果相等就立刻停止服务,不相等不停止服务。
11.5 Android 的线程池
线程池的优点
1. 重用线程池的线程,避免因为线程的创建和销毁带来的性能开销
2. 能有效控制线程池的最大并发数,避免大量线程因抢占系统资源而导致的阻塞现象。
3. 能够对县城进行简要管理,并提供定时执行以及间隔循环执行等功能。
ThreadPoolExecutor
ThreadPoolExecutor
是线程池的真正实现,提供了一系列参数配置线程池。
/**
* 核心线程数 默认情况下,核心线程会在线程池中一直存活,即使处于闲置状态。
*/
private volatile int corePoolSize;
/**
* 线程池所能容纳的最大线程数,当活动线程数达到这个数值之后,后续的新任务将会被阻塞
*/
private volatile int maximumPoolSize;
/**
* 如果将该属性置为true,闲置的核心线程等待新任务到来的时候会有超时策略,时间间隔由keepAliveTime决定
*
* 当等待时间超过keepAliveTime指定的时间之后,核心线程就会停止
*/
private volatile boolean allowCoreThreadTimeOut;
/**
* 当allowCoreThreadTimeOut不为true的时候,非核心的线程闲置的超时时长,超过这个时长,非核心线程就会回收
*
* 当allowCoreThreadTimeOut为true的时候,同样作用于核心线程。
*/
private volatile long keepAliveTime;
/**
* 线程工厂,为线程池提供创建新线程的功能,
*
* ThreadFactory是一个接口,只有一个方法newThread(Runnable r)
*/
private volatile ThreadFactory threadFactory;
/**
* 线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中
*/
private final BlockingQueue<Runnable> workQueue;
/**
* 当线程池无法执行任务的时候,这可能是由于任务队列已满或者是无法成功执行任务,
* 此时ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution
* 方法通知调用者,默认情况下抛出RejectedExecutionException异常。
*/
private volatile RejectedExecutionHandler handler;
ThreadPoolExecutor 执行任务的规则
1. 如果线程池中的线程数量未达到核心线程数,那么会直接启动一个核心线程来执行任务。
2. 如果线程池中的线程数量已经达到或者数量超过核心线程的数量,那么任务就会被插入到任务队列中排队等待执行。
3. 如果步骤 2 中无法将任务插入都任务队列中,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定的最大值,那么会立即启动一个非核心线程来执行任务。
4. 如果步骤3中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务,此时ThreadPoolExecutor
会调用RejectedExecutionHandler
的rejectedExecution
方法通知调用者。
11.6 线程池的分类
FixedThreadPool:
1.只有核心线程,且线程数量固定当线程处于空闲状态
2.不会被回收,除非线程池被关闭了
3.任务队列没有限制
CachedThreadPool
1. 核心线程数为0,非核心线程数量不固定,最大线程数可以任意大
2. 线程池中的空闲线程有超时机制,为60秒
ScheduledThreadPool
1. 核心线程数量固定,非核心线程数量没有限制
2. 非核心线程闲置的时候会被立即回收
3. 主要用于执行定时任务和具有固定周期的重复任务
SingleThreadExecutor
1. 只有一个核心线程,确保所有的任务都在同一个线程中按顺序执行
2. 统一所有的外界任务到一个线程中,使得任务之间不需要处理线程同步的问题