线程与线程池
概括
线程分为主线程和子线程. 主线程主要是用来处理和界面相关的事情, 子线程主要是用来做耗时的操作,比如 加载远程数据,数据库操作等。
在android 中,处理直接使用 Thread以外。 android 还提供了很多类似线程的操作便于我们开发, 有 AsyncTask 和 IntentService ,还有 HandlerThread. 他们都有各自的特性。 AsyncTask 底层使用的是 handler + 线程池。 而 IntentService 和 HandlerThread 内部则 直接使用的线程。
IntentService 的优点是 它是以Service 组件的形式存在,当在后台执行任务时,不容易被系统杀死。
AsyncTask
AsyncTask 封装了 Handler 和 线程池。 所以能够更加方便的在后台执行任务,轻松的切换到主线程更新UI。
AsyncTask 是一个抽象类,提供 Params 、Progress 和 Result 三个泛型参数。
public abstract class AsyncTask<Params, Progress, Result> {}
如果不需要传递参数,可以使用 Void 来代替。
AsyncTask 有四个核心方法:
- onPreExecute() : 运行在Ui 线程,执行在 doInBackground 之前,一般做些准备工作。
/**
* Runs on the UI thread before {@link #doInBackground}.
*
* @see #onPostExecute
* @see #doInBackground
*/
protected void onPreExecute() {
}
doInBackground(Params… params) 这个方法是在子线程中执行。可以用来执行计算、访问网络等耗时操作, params 表示 执行任务所需要的参数。 在此方法中可以通过 publishProgress 来更新任务的进度。 publishProgress会引起 onProgressUpdate方法的回调。 doInBackground 需要返回结果给 onPostExecute。
onProgressUpdate(Progress… values) 运行在ui 线程。当任务执行进度发生改变时,会被调用。
onPostExecute(Result result) 运行在ui线程, 当异步的任务执行完毕,会把结果传递给它。
上述几个方法的执行顺序是: onPreExecute() -> doInBackground -> onPostExecute.
AsyncTask 还提供了 onCancelled()方法,运行在主线程。 当任务被取消时,该方法会被回调。这个时候 onPostExecute 则不会被调用。
··· 为可变参数
AsyncTask 必须在主线程中创建,其 execute 也必须在主线程中执行。 Android 4.1后的版本,系统已经自动完成。在Android5.0的源码中 ActivityThread 的 main 方法中,会调用 init方法。
AsyncTask.init();
不要在程序中直接调用 onPreExecute…等的方法,一个AsyncTask对象只能执行一次,即只能调用一次 execute方法,否则会抛出异常。
在1.6之前是 AsyncTask 是串行执行任务的, 从1.6开始 采用的是并行的,但从3.0开始,为了避免AsyncTask 所带来的并发错误, Async 又采用了 串行来执行任务。 但我们任然可以通过 executeOnExecutor 来并行执行任务
AsyncTask 源码分析
在 execute 方法中, 调用了 executeOnExecutor
:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
我们来看 executeOnExecutor
的代码:
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
...// 省略代码
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
sDefaultExecutor是一个串行的线程池。 所有的AsyncTask 都在这里排队执行。 从上面的代码看出,首先是 onPreExecute 先执行。
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
上面看到 是调用了 exec.execute
即 SerialExecutor 的实现。
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
从上面的代码可以分析出 AsyncTask 是排队执行的。 在AsyncTask的构造函数中,系统会把 Params 封装成 FutureTask 对象。 FutureTask 是一个并发类,在这里充当了 Runnable。 SerialExecutor 的execute 方法首先会把 FutureTask对象插入到 任务队列 mTask 中,然后执行 scheduleNext()方法。当任务执行完毕后,会调用 finally 部分,继续执行其他的任务,直到所有的任务都执行完毕。从这点可以看出 AsyncTask是 串行执行的。
AsyncTask 内部有两个线程池 SERIAL_EXECUTOR 和 THREAD_POOL_EXECUTOR, 还有一个Handler(InternalHandler)。 SERIAL_EXECUTOR 是用来进行 任务的排队。 而 THREAD_POOL_EXECUTOR 是真正在执行任务的线程池。 InternalHandler 负责进行线程的切换。
在上面的代码中,会调用 FutureTask的run 方法。而 FutureTask分装了 mWorker(内部有Params的引用),会调用mWorker 的 call 方法。所以mWorker的 Call方法会在线程池中执行。 下面是代码:
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
mTaskInvoked 设为true,表示 当前任务已经被调用过了。 然后执行 AsyncTask的 doInBackground.然后把其返回值传递给 postResult。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
会发送一个消息到 InternalHandler. 该消息的处理为一下代码。
result.mTask.finish(result.mData[0]);
result.mTask即 AsyncTask。 所以我们来看 finish 方法。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
这里呢,如果任务被取消了就调用 onCancelled,否则就会调用 onPostExecute方法。 而doInBackground 的结果会传递到 onPostExecute。
到这里的分析就结束了。
HandlerThread
HandlerThread 继承了 Thread, 它是一种可以使用Handler的 Thread 实现,在run 方法中 通过 Looper.prepare()的方式创建消息队列, 通过Looper.loop()开启消息循环。 这样的话,就可以在 HandlerThread中创建 Handler了。
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
上面是其run 方法的实现。
区别于普通的Thread: 普通的Thread是使用run方法来执行耗时操作, HandlerThread的内部却是创建了消息的队列。 外界需要通过 Handler的消息方式来通知 HandlerThread 执行一个具体的任务。
当我们不需要使用的时候,可以通过 quit 或者 quitSafely方法来终止线程的执行
具体的使用场景: IntentService.
IntentService
IntentService是一种特殊的Service。 继承自Service 并且是一个抽象类。 区别于普通的Service,IntentService可以用于执行后台耗时的操作。 而且执行完毕后,会自动停止。
IntentService 与 普通的线程 : 相比之下,IntentServie 是一个服务组件,优先级比较高,不容易被系统杀死。
IntentService的内部 封装了 HandlerThread 和 Handler 。 我们来看 IntentService的 onCreate方法:
@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
当IntentService 被启动,其onCreate方法会被调用, 会创建 HandlerThread, 并获取它的 Looper来构建 Handler对象,即 mServiceHandler。 mServiceHandler 所发送的消息 最终都会在 HandlerThread 中执行。
每次启动 IntentService 它的 onStartCommand 都会被调用一次。 在onStartCommand 中可以获取到 任务的 Intent 参数。
在 onStartCommand 中又调用了 onStart.
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
从上面可以看到 在onStart 中 发送了一个消息到 mServiceHandler。 在Handler 内部 又会将 Intent参数传递给 onHandleIntent
去处理。 onHandleIntent是发生在 HandlerThread 线程。
这个Intent对象的内容和外界的startService(intent) 中的 intent的内容是完全一致的, 通过这Intent 可以解析到外界所 传递的参数。通过这个参数可以区分具体的后台任务
当任务结束后,会调用 stopSelf(int startId) 来停止服务。
注意 stopSelf() 和 stopSelf(int startId)
stopSelf()会立即停止服务 , 而stopSelf(int startId) 在尝试停止之前会判断最近启动服务的次数是否和 startId相等。如果相等则会立即停止服务,不相等则不停止(可以从 AMS的 stopServiceToken 的实现中找到依据)。
ServiceHandler的代码:
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
从上面的整体流程中,可以知道 IntentService 是串行的。 按照外界发起的顺序排队执行。
Android中的线程池
线程池的好处:
1.重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。
2.能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统志愿而导致的阻塞现象。
3.能够对线程进行简单的管理,并指定定时执行 以及 指定间隔循环执行等功能。
Android中线程池的概念来自于 Java中的 Executor, Executor是一个接口,真正的线程实现为ThreadPoolExecutor.
ThreadPoolExecutor
提供了很多参数可以来配置线程池。 Android还提供了4类线程池(可以通过提供的工厂方法获得),内部都是通过配置ThreadPoolExecutor
来实现的。
ThreadPoolExecutor 是线程池的真正实现,下面是一个比较常用的构造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
corePoolSize: 线程池的核心线程数,默认的情况下,核心线程会在线程池中一直存活,即使是处于空闲的状态, 如果将 ThreadPoolExecutor 的 allowCoreThreadTimeOut 设置为 true, 那么闲置的核心线程在等待任务时会有超时策略,这个是由 keepAliveTime 来指定。 当超过时,核心线程就会被终止。
maximumPoolSize: 线程池所能容纳的最大线程数,当线程池到达这个数值后,后续的新任务将会被阻塞。
keepAliveTime: 非核心线程闲置时的超时时长, 超过这个时长, 非核心线程就会被回收。当 allowCoreThreadTimeOut 设置为 true 时, keepAliveTime 同样会作用于核心线程。
unit 用于指定keepAliveTime 参数的时间单位, TimeUnit 是一个枚举, 常用的有 MILLISECONDS、SECONDS、MINUTES 等等。
workQueue: 线程池中的任务队列, 通过 线程池的 execute方法所提交的Runnable 对象都会存储在这个参数中。
threadFactory: 线程工厂,为线程池提供创建新线程的功能。 ThreadFactory 是一个接口,它只有一个方法 Thread newThread(Runnable r);
除了上面的,还有个不常用的参数 RejectedExecutionHandler,当线程池无法执行任务时,会通过调用handler的 rejectedExecution 方法来通知调用者。 默认情况下是 抛出一个 RejectedExecutionException 的异常。 还有一些 其他的可选值 策略 AbortPolicy 、DiscardPolicy 等. 默认是 AbortPolicy,即抛出一个异常。
线程池的配置方式可以参考 AsyncTask:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
从上面可以得出:
- 核心线程数等于CPU核心数+1;
- 线程池的最大线程数为CPU数的2倍+1.
- 核心线程无超时机制,非核心线程在闲置时的超时时间为 1秒;
- 任务队列的容量为128.
线程池的分类
Android中常见的四类线程 可以通过 Executors
的工厂方法获取到 分别是 FixedThreadPool 、CachedThreadPool、ScheduledThreadPool 、SingleThreadExecutor. 它们的内部都是通过直接或者间接的配置 ThreadPoolExecutor 来实现的。
FixedThreadPool
它是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了,当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来。由于 FixedThreadPool 只有核心线程 并且这些核心线程不会被回收,所以能够更快速的响应外界的请求。 这写 核心线程是没有超时机制的, 任务队列也没有大小的限制。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
CachedThreadPool
通过 Executors
newCachedThreadPool 方法来创建,它是一种数量不定的线程池,而且 只有非核心线程, 并且最大的线程数是 Integer.MAX_VALUE.是一个很大的数,相当于是 任意大。 当线程池的线程都处于工作状态是, 线程池会创建新的线程来处理新任务。 线程池中空闲线程是有超时机制的,时长为 60s。超过 60s的闲置线程,就会被回收。CachedThreadPool的任务队列相当于一个空的集合, 这会导致任何任务都会被立即执行。SynchronousQueue
很多情况下 可以把它简单理解为一个无法存储元素的队列。
当线程池中的线程都处理闲置状态时, 线程都会超时而被停止。这个时候线程池实际上是没有任何线程的,几乎不占用系统的资源。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
ScheduledThreadPool
通过 Executors
的 newScheduledThreadPool
方法来创建,它的核心线程数是固定的。而非核心线程数是没有限制的。 非核心线程的超时策略中 keepAliveTime
0 . 即代表只要 线程一闲置,就会被立即回收掉。 这中线程池主要用来执行定时任务和具有固定周期的重复任务。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
SingleThreadExecutor
通过 Executors
的 newSingleThreadExecutor
方法来创建。这个线程池内部只有一个核心线程。并且最大线程数也是1 。它确保所有的任务都在同一个线程中按顺序执行。 SingleThreadExecutor
在于统一外界所有的任务到一个线程中。使得这些任务不需要处理同步的问题。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
到这线程池的相关内容就讲完了。