Android线程与线程池

Android线程与线程池

Android小彩虹2021-08-17 11:22:08280A+A-

在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开发艺术探索》

www.cnblogs.com/absfree/p/5…

blog.csdn.net/carson_ho/a…

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示 | 支付宝红包
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1
本网站由 提供CDN/云存储服务

联系我们