Android的线程和线程池
主线程和子线程
-
Android
中的主线程也叫UI线程,主要作用是运行四大组件以及处理他们和用户的交互。 -
Android
中的子线程的作用是执行耗时任务, 比如网络请求,I/O操作等。
Android中的线程状态
除了传统的Thread
外,还包含AsyncTask
,HandlerThread
,IntentService
。
1. AsyncTask
AsyncTask
其实是封装了Handler
和Thread
, 方便了我们的使用。
-
AsynTask
是一个抽象的泛型类,它提供了Params
,Progress
和Result
这三个泛型参数,其中-
Params
表示参数类型, -
Progress
表示后台任务的执行进度的类型, -
Result
则表示后台返回结果
-
-
AsyncTask
提供了4个核心方法,含义如下-
onPreExecute()
在主线程中执行,在异步任务执行之前,此方法会被调用 -
doInBackground(Parms… params)
在线程池中执行,此方法用于执行异步任务,params
参数表示异步任务的输入参数 -
onProgressUpdate(Progress… values)
在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用 -
onPostExecute(Result result)
在主线程中执行,在异步任务执行之后,此方法会被调用,其中result
参数是后台任务的返回值.
执行的顺序是
onPreExecute()
先执行,接着是doInBackground()
,最后才是onPostExecute()
。
PS:(有种情况是AsyncTask()
还提供了onCancelled()
方法,它同样在主线程中执行,当异步任务取消时,onCancelled()
方法会被调用,这个时候onPostExecute()
则不会被调用) -
-
AsyncTask
具体有几点限制-
AsyncTask
第一次访问必须在主线程中加载,在android4.1及以上版本中已经被系统自动完成,和在android5.0源码中可以看出,在ActivityThread
的main()
方法,它会调用AsyncTask
的init()
方法,满足了在主线程中加载的条件。 -
AsyncTask
的对象必须在主线程中创建 -
execute
必须在UI线程调用(因为excute()方法中会调用onPreExecute()) - 一个
AsyncTask
对象只能执行一次,即只能调用一次execute
方法 - 在Android1.6以前,AsyncTask是串行执行的,Android1.6的时候开始采用并行执行的方式,但是从Android3.0开始,为了避免AsyncTask所带来的兵法错误,AsyncTask又采用一个线程来串行执行任务。尽管如此,我们还是通过executeOnExecutor()方法来并发的执行任务。
-
2. HandlerThread
HandlerThread
继承了Thread
,在其run
方法中通过Looper.prepare()
方法来创建消息队列,并通过Looper.loop()
来开启消息循环,这样在实际的使用中就允许在HandlerThread
中创建Handler
了。在不需要使用HandlerThread
时,可以通过他的quit
或者quitSafely
方法来终止线程的执行
// HandlerThread run()方法
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
//使用方法
HandlerThread handlerThread = new HandlerThread("MyHandler");
handlerThread.start();
Handler customHandler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 处理Message
}
};
3. IntentService
-
IntentService
是服务的原因,这导致它的优先级比单纯的线程要高很多。其中它封装了HandlerThread
和Handler
. - 它第一次启动会在
OnCreate
方法中创建一个HandlerThread
, 并在其中构造了一个mServiceHandler
。由于mServiceHandler
发送的消息都在HandlerThread
中,所以IntentService
也可以执行后台任务。之后,mServiceHandler
收到消息 后,会将Intent
对象传递给onHandleIntent
方法去处理。一般来说停止一个该服务用stopSelf(int startId)
,是因为stopSelf(int startId)
会等到所有的消息都处理完毕后才终止服务。 - 它也是顺序执行后台任务。
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);
}
}
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
Android中的线程池
使用线程池有以下好处:
- 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销
- 能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占资源而导致的阻塞现象
- 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能
Andorid中的线程池都是直接或者间接通过配置ThreadPoolExecutor来实现的
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
-
corePoolSize
: 线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使他们处于闲置状态(如果将ThreadPoolExecutor
的allowCoreThreadTimeOut
参数设为true
,那么闲置的核心线程也会受keepAliveTime
参数影响被回收) -
maximumPoolSize
:线程池所能容纳的最大线程数,当活动线程数达到这个数值后,后续的新任务将会被阻塞。 -
keepAliveTime
:非核心线程闲置时的超时时长,超过这个时长,非核心线程就会回收。 -
unit
:用于指定keepAliveTime
参数的时间单位 -
workQueue
:线程池中的任务队列,通过线程池的execute
方法提交的Runnable
独享会存储在这个参数中 -
threadFactory
:线程工厂,为线程池提供创建新线程的功能
ThreadPoolExecutor
执行任务时大致遵循如下规则:
- 如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
- 如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行。
- 如果在步骤2中无法将任务插入到任务队列中,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行任务。
- 如果步骤3中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务,
ThreadPoolExecutor
会调用RejectedExecutionHandler
的rejectedExecution
方法来通知调用者。
AsyncTask
的THREAD_POOL_EXECUTOR
线程池(执行任务的线程池)的配置:
-
corePoolSize
= CPU核心数+1; -
maximumPoolSize
= 2倍的CPU核心数+1; - 核心线程无超时机制,非核心线程在闲置时间的超时时间为1s;
- 任务队列的容量为128。
线程池的分类:
- FixedThreadPool
线程数量固定的线程池,它只有核心线程并且这些核心线程不会被回收,能够更快地响应外界的请求。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(
nThread, nThread, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()
);
}
- CachedThreadPool
线程数量不固定的线程池,它只有非核心线程,比较适合执行大量的耗时较少的任务。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(
0L, Interger.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()
);
}
- ScheduledThreadPool
核心线程数量固定,非核心线程数量没有限制的线程池,主要用于执行定时任务和具有固定周期的重复任务。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Interger.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
}
- SingleThreadExecutor
只有一个核心线程的线程池,确保了所有的任务都在同一个线程中按顺序执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService(
new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>())
);
}