Android进阶(11)| 线程和线程池

12070003-73354c06cc7f06f1.png
本节目录

一.主线程和子线程

主线程:主线程在Android中也叫做UI线程主线程的作用是运行四大组件以及处理它们与用户的交互。在默认情况下一个进程只有一个线程,而这个线程就是主线程。

子线程:子线程的作用就是执行耗时任务,比如网络请求,I/O操作等。从Android 3.0开始系统要求网络访问操作必须要在子线程完成,否则将会抛出异常。

二.Android中线程的形态

除了传统的Thread以外,线程还有AsyncTask、HandlerThread和IntentService这三种特殊的形态,这三者的底层实现也是线程,只不过是具有特殊的表现形式,下面来分别介绍:

1.AsyncTask

概念:AsyncTask是一种封装了Thread和Handler的轻量级异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终的结果传递给主线程并在主线程中更新UI。同时AsyncTask是一个抽象的泛型类,它的定义如下:

public abstract class AsyncTask<Params,Progress,Result>

内部方法:在AsyncTask中提供了5种核心的方法,如下所示:

  • onPreExecute()
    在主线程中执行,在异步任务执行之前此方法会被调用,一般可以做一些准备工作。

  • doInBackground(Params...params)
    在线程池中执行,此方法用于执行异步任务,params表示异步任务的输入参数。此方法可以通过publishProgress方法来更新任务进度。此外此方法需要返回计算结果给onPostExecute方法。

  • onProgressUpdate(Progress...values)
    在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用。

  • onPostExecute(Result result)
    在主线程中执行,在异步任务执行之后,此方法会被调用,其中result参数是后台任务的返回值,即doInBackground方法的返回值。

  • onCancelled()
    在主线程中执行,当异步任务被取消时,onCancelled()方法会被调用。当该方法被调用后onPostExecute不会被调用。

工作原理:当我们新建了一个AsyncTask对象之后,就可以使用execute方法来进行执行,同时在execute中又会去调用executeOnExecutor方法,在该方法中会去调用onPreExecute()方法,接着会去开启一个sDefaultExecutor的串行的线程池,在线程池中会去调用mWorker的call方法,而在call方法中会去调用postResult方法,在postResult方法中会通过sHandler发送了一个MESSAGE_POST_RESULT的消息,当sHandler收到MESSAGE_POST_RESULT这个消息后会调用AsyncTask的finish方法,该方法的具体实现如下:

private void finish(Result result){
    if(isCancelled()){
        onCancelled(result);
    }
    else{
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}

2.HandlerThread

概念:HandlerThread继承了Thread,它是一种可以使用Handler的Thread,在它的内部会进行Looper的创建以及开启。

内部实现:Handler的实现也很简单,就是在run方法中通过Looper.prepared()来创建消息队列,并通过Looper.loop()来开启消息循环,具体实现如下:

public void run(){
    mTid = Process.myTid();
    Looper.prepare();
    synchronized(this){
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

3.IntentService

概念:IntentService是一种特殊的Service,它继承了Service并且是一个抽象类。IntentService可用于执行后台耗时的任务,同时由于IntentService是一个服务的原因使得它的优先级比单纯的线程要高出很多。

工作流程:当IntentService被第一次启用时,它的onCreate()方法会被调用,在onCreate()方法中会创建一个HandlerThread。每次启动IntentService,它的onStartCommand方法就会被调用一次,IntentService在onStartCommand中处理每个后台任务Intent,onStartCommand()方法会调用onStart(),具体如下:

public void onStart(Intent intent,int startId){
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}

在onStart()方法中会给mServiceHandler发送一条消息,当mServiceHandler接受到消息之后就会将Intent对象传递给onHandleIntent方法去处理,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);  //执行结束,停止服务
    }
}

上述代码中onHandleIntent是一个抽象方法,它需要我们在子类中去实现,它的作用是从Intent参数中区分具体的任务并去执行这些任务。关于stopSelf方法,它会在最后一个任务执行完成之后才会去停止服务。

三.Android中的线程池

概念:线程池简单来说就是会缓存一定数量的线程,通过线程池就可以避免因为频繁创建和销毁线程所带来的系统开销。Android中线程池的概念来源于Java中的Executor,它是一个接口,它的真正实现是ThreadPoolExecutor,ThreadPoolExecutor提供了一系列参数来配置线程池,通过不同的参数可以创建不同的线程池。

1.ThreadPoolExecutor

构造方法:ThreadPoolExecutor的构造方法提供了一系列参数来配置线程池,它的构造方法定义如下:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory)
  • corePoolSize
    线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态。如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么当延时超过keepLiveTime之后,核心线程就会被终止。

  • maximumPoolSize
    线程池所能容纳的最大的线程数,当活动线程数达到这个数值之后,后续的任务将会被阻塞。

  • keepLiveTime
    默认为非核心线程闲置超时时长,超过这个时长,非核心线程就会被回收。

  • unit
    用于指定keepLiveTime参数的时间单位,这是一个枚举。

  • workQueue
    线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。

  • threadFactory
    线程工厂,为线程池提供创建新线程的功能。ThreadFactory是一个接口,它只有一个方法:Thread newThread(Runnable r);

2.ThreadPoolExecutor执行规则

(1)如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
(2)如果线程池中的线程数量达到核心线程的数量,那么任务会被插入到任务队列中排队等待执行。
(3)如果在(2)中无法将任务插入到任务队列中,这时就是任务队列已满,这时如果线程数量未达到线程池所规定的最大值,那么就会启用一个非核心线程来执行任务。
(4)如果线程数量已经达到了线程规定池规定的最大值,那么就会拒绝执行此任务。

3.线程池的分类

1.FixedThreadPool
它是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收。当所有的线程都处于活动状态时,新任务都会处于等待状态。FixedThreadPool只有核心线程并且这些核心线程都不会被回收,因此它能够更快的相应外界的请求。

public static ExecutorService newFixedThreadPool(int nThreads){
    return new ThreadPoolExecutor(nThreads,nThreads,OL,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}

2.CachedThreadPool
该类线程是一种数量不定的线程池,它只有非核心线程,而它内部的最大线程数可以近似于任意大,而它内部对空闲线程有超时机制,并且超时时长为60s,超过时间线程就会被收回。由于这些特性,可以得到该线程内部的任务队列其实相当于一个空集合,这将导致任何任务都会被立即执行。该类线程比较适合执行大量的耗时较少的任务。

public static ExecutorService new CachedThreadPool(){
    return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}

3.ScheduleThreadPool
该类线程的核心数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收。该线程池主要用于执行定时任务和具有固定周期的重复任务。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

public ScheduledThreadPoolExecutor(int corePoolSize){
    super(corePoolSize,Integer.MAX_VALUE,0,NANOSECONDS,new DelayedWorkQueue());
}

4.SingleThreadExecutor
该类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行。SingleThreadExecutor的意义在于统一所有外界任务到一个线程中,这使得在这些任务之间不需要处理线程同步的问题。

public static ExecutorService newSingleThreadExecutor(){
    return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1,1,
                                                                          OL,TimeUnit.MILLISECONDS,
                                                                          new LinkedBlockingQueue<Runnable>()));
}

转载于:https://www.jianshu.com/p/06c9f579867a

猜你喜欢

转载自blog.csdn.net/weixin_34037977/article/details/91118094