Android线程与线程池

因为要写团队的推文所以写了这个,很多东西都参考自《Android开发艺术探索》这本书,姑且当做自己的学习笔记。感觉自己还需要学很多东西,要有自己的东西和思想。

在Android中,线程分为主线程和子线程,主界面用于与用户交互,进行UI的相关操作,而子线程则负责耗时操作。如果在主线程中进行耗时操作,就会使程序无法及时的响应。因此耗时操作必须放在子线程中进行。

1、主线程和子线程

主线程是指进程所有用的线程,在Android中即指进行UI与用户交互的线程就是主线程。因此在Android开发中,需要竟可能的把耗时操作,网络请求访问操作,数据库读取操作等放在子线程,以避免主线程长期处于占用状态以降低用户体验。系统要求网络访问必须在子线程中进行,否则会抛出NetworkOnMainThreadException异常。

2、线程形态

Android中的线程形态有传统的Thread,AsyncTask,HandlerThread和IntentService。

2.1、AsyncTask

AsyncTask封装了Thread和Handler,必须在主线程进行调用,它可以在子线程中执行任务,然后将执行的结果传递给主线程并更新UI。但AsyncTask并不适合执行特别耗时的任务。

2.1.1、参数:

AsyncTask是一个泛型类,提供了三个泛型参数,Params ,Progress 和Result。

  • Params表示参数的类型
  • Progress表示后台任务的执行进度的类型
  • Result表示后台任务返回结果的类型

AsyncTask的声明:

public abstract class Asynctask<Params,Progress,Result>

2.1.2、方法:

AsyncTask提供了一些核心方法:

  • onPreExecute() 在主线程中调用用来进行异步任务的准备操作。
  • doInBackground(Params …… params) 在执行完onPreExecute()后进行子线程任务时自动调用,Params表示异步任务的输入参数。在方法中可以通过publishProgress更新任务的完成进度,同时在结束调用后会返回结果给onPostExecute()方法。
  • onProgressUpdate(Params …… params) 在主线程中用于显示任务进行的进度,在publishProgress()方法中被调用。
  • onProgressExecute(Result result) 在主线程中用户获取任务结束后回返的结果,即doInBackground的返回值。
  • onCancelled() 在主线程中执行,当异步任务被取消后不会执行onProgressExecute方法而会执行onCancelled方法。

2.1.3、调用:

AsyncTask的使用步骤如下:
1.创建AsyncTask子类,具体实现其核心方法
2.创建子类的实例对象
3.调用execute执行异步线程任务

2.1.4、AsyncTask的限制:

1.必须在主线程加载,即第一次访问AsyncTask必须在主线程,对象必须在主线程创建
2.execute必须在UI线程中调用
3.不能再程序中直接调用onPreExecute(),onPostExecute(),doInBackground和onProgressUpdate方法。
4.一个AsyncTask方法只能使用一次

2.1.5、AsyncTask的工作原理:

  • execute方法
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
}

public final AsyncTask<Params,Progress,Result>executeOnExecutor(Executor exec, Params...params){
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:
                throw new IllegalStateException("Cannot execute task:"
                    + " the task is already running.");
            case FINISHED:
                throw new IllegalStateException("Cannot execute task:"
                    + " the task has already been executed "
                    + "(a task can be executed only once)");
        }
    }
    mStatus=Status.RUNNING;
    onPreExecute();
    mWorker.mParams=params;
    exec.execute(mFuture);
     return this;
}

在execute方法中,会检测AsyncTask的当前状态,如果当前为RUNNING或FINISHED状态,系统会抛出异常,当为空闲状态,则置为RUNNING状态。
置为RUNNING状态后会调用onPreExecute方法,同时将参数Params传递给mWorker的mParams。之后调用exec.execute,并传入mFuture,其中exec就是传进来的sDefaultExecutor。

  • sDefaultExecutor
    sDefaultExecutor是一个串行的线程池,一个进程中的所有的AsyncTask全部在这个串行线程池中排队执行,在executeOnExecute方法中,onPreExecute方法最先执行。
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

public static final Executor SERIAL_EXECUTOR = new 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);
        }
    }
}

mTasks代表了SerialExecutor这个串行线程池的任务缓存队列,之后用offer向任务缓存队列中添加了一个任务,调用了r的run方法,r就是传入的mFuture,而mFuture的run方法内部会调用MWorker的call方法,接着调用doInBackground方法,开始执行后台任务。执行结束后会调用scheduleNext方法,执行下一个任务。另外mActive表示了AsyncTask的对象,如果MActive为null,则同样会执行scheduleNext方法。在scheduleNext方法中,若缓存队列不为空,则从队列中取出任务执行。
综上,SerialExecutor的工作是将任务加入缓存队列中,而执行任务的是THREAD_POOL_EXECUTOR。

  • THREAD_POOL_EXECUTOR
public static final Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

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);

即corePoolSize的CPU数加一,maxumumPoolSize的CPU数二倍加一,存活1s,任务缓存队列为LinkedBlockingQueue。

  • postResult方法
private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

首先用getHandler方法获取AsyncTask获取内部包含的sHandler,然后发送MESSAGE_POST_RESULT消息。

  • sHandler
private static final InternalHandler sHandler = new InternalHandler();
 
private static class InternalHandler extends Handler {
    public InternalHandler() {
        super(Looper.getMainLooper());
    }
 
    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
} 

sHandler是一个静态的Handler对象,为了sHandler能够执行环境从后台切换到主线程,则应使用主线程的Looper,在主线程中创建sHandler。sHandler收到MESSAGE_POST_RESULT后,会调用finish方法。源码如下:

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

调用isCancelled()判断任务是否取消,如果取消则调用 onCancelled(result),否则调用onPostExecute(result)。同时将mStatus设为FINISHED,表示对象执行完毕。

2.2、HandlerThread

2.2.1、简介及实现

HandlerThread继承了Thread,能够使用Handler,实现简单,节省系统资源开销。
实现如下:

HandlerThread thread = new HandlerThread("MyHandlerThread");
thread.start();
mHandler = new Handler(thread.getLooper());
mHandler.post(new Runnable(){...});

2.2.2、源码及分析

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    protected void onLooperPrepared() {
    }

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

    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

}

它创建了一个消息队列,外界需要通过Handler的消息通知它执行一个任务,由于HandlerThread的run方法是一个无限循环方法,所以可以通过quit方法终止线程的执行。

2.3、IntentService

IntentService是特殊的Service,继承了Service,因为IntentService是一个抽象类,所以必须创建IntentService的子类才能使用。
同时,IntentService是服务,所以在执行时,优先级较高。
IntentService封装了HandlerThread和Handler。

2.3.1、使用

1.定义IntentService子类,传入线程名称,复写onHandlerIntent()方法。
2.在Manifest.xml中注册
3.在Activity中开启服务

2.3.2、源码分析

  • onCreate
    在IntentThread被第一次启动时,会调用相应的onCreate方法。具体如下:
@Override
public void onCreate() {
    super.onCreate();
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();
  
    
    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper); 
}

首先实例化新线程,然后获得工作线程的 Looper,并且构造一个Handler对象mServiceHandler,通过mServiceHandler发送的消息都会在HandlerThread中执行。

  • onStartCommand

每次启动IntentService都会调用onStartCommand方法,用于处理每个后台任务的Intent。

public int onStartCommand(Intent intent, int flags, int startId) {

    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

public void onStart(Intent intent, int startId) {

    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}

可以看出在onStartCommand中会调用onStart 方法,在onStart方法中,获得ServiceHandler消息的引用,后将消息包装到msg中,之后发送消息。这个消息会在HandlerThread中被处理。

  • 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的onHandleIntent方法是一个抽象方法,需要实现,作用是从Intent中区分具体任务并执行,调用执行完任务后,stopSelf会直接停止服务,当存在多个任务时,stopSelf则会在最后一个任务执行完毕后停止服务。

3、线程池

在实际使用中,线程池往往更加放标,和线程相比,线程池性能开销小,可避免因为大量线程抢占资源而堵塞,也更容易管理。

java中线程池的接口是ExecutorService,默认实现是ThreadPoolExecutor。

3.1、ThreadPoolExecutor

ThreadPoolExecutor是线程池的实现,构造方法如下

public ThreadPoolExecutor(int corePoolSize,
                        int maximumPoolSize,
                        long keepAliveTime,
                        TimeUnit unit,
                        BlockingQueue<Runnable> workQueue,
                        ThreadFactory threadFactory)
  • corePoolSize

核心线程数,核心线程会在线程池中一直存活。若将allowCoreThreadTimeOut设置为true,那么核心线程也将有timepout,会在超时后被杀死。

  • maximumPoolSize

最大线程数。

  • keepAliveTime

非核心线程限制的超时时长,超过设置时间后非核心线程会被收回,allowCoreThreadTimeOut设置为true后核心线程也会被收回。

  • unit
    时间的参数单位

  • workQueue

任务队列,通过线程池的execute方法提交的Runnable对象储存在其中

  • threadFactory

线程工厂,为线程池创建线程

ThreadPoolExecutor在执行线程任务是需满足一定的规则

  • 首先使用核心线程,在核心线程不够时才启用新线程
  • 当任务书大于核心线程数量,那么会被插入等待队列中
  • 如果不能插入等待队列,且线程数量未达到最大线程数,则会开启新线程
  • 若无法插入等待队列且无法创建新线程,则请求会被拒绝

3.2、线程池的分类

线程池分为四种不同的功能,分别是FixedThreadPool、CachedThreadPool、ScheduledThreadPool、SingkeThreadExecutor。

  • FixedThreadPool

通过execute的newFixedThreadPool方法创建,固定大小的线程池,每次提交任务都会创建新的线程,直到池中线程数目达到最大。如果有一个线程异常结束后,会产生一个新的线程补充进去。能够更快地相应外界请求。
具体实现如下:

public static ExecutorSrevice newFixedThreadPool(int nThreads){
    return new ThreadPoolExecutor(nThread,nThreads,
                            0L,TimeUnit.MILLISECINDS,
                            new LinkedBlockingQueue<Runnable>());
}
  • CachedThreadPool
    一个可以根据需要创建线程的线程池,对于很多短期的异步任务,将有效提高性能,调用execute()将重用已构造的线程,如果没有线程可用,将创造一个新的线程并加入线程池,并移除超过60s未使用的线程。适合执行耗时少的任务。
    具体实现如下:
public static ExecutorService newCachedThreadPool(){
    reutrn new ThreadPoolExecutor(0,Interger.MAX_VALUE,
                            60L,TimeUnit.SECONDS,
                            new SynchronousQueue<Runnable>());
}
  • ScheduledThreadPool
    核心线程有限,非核心线程数无线,创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求。
    具体实现如下:
public static ScheduledThreadPool newScheduledThreadPool(int corePoolSize){
    reutrn new ScheduledThreadPool(corePoolSize);
}

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

  • SingkeThreadExecutor
    创建单线程池,即线程池中只有一个线程,所有的任务是串行执行,如果池中线程因异常结束,会有一个新的线程来代替。以此保证了任务按提交顺序执行。
    具体实现如下:
public static ExecutorService newScheduledThreadExecutor(){
    reutrn new FinalizeableDelegatedExecutorService
            (new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECINDS,new LinkedBlockingQueue<Runnable>()));
}

参考来源:

《Android开发艺术探索》
https://www.cnblogs.com/absfree/p/5357678.html
https://blog.csdn.net/carson_ho/article/details/53407806

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容