Android中的线程与线程池

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mike_Cui_LS/article/details/81298748

线程与线程池

概括

线程分为主线程和子线程. 主线程主要是用来处理和界面相关的事情, 子线程主要是用来做耗时的操作,比如 加载远程数据,数据库操作等。

在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_EXECUTORTHREAD_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

通过 ExecutorsnewScheduledThreadPool 方法来创建,它的核心线程数是固定的。而非核心线程数是没有限制的。 非核心线程的超时策略中 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

通过 ExecutorsnewSingleThreadExecutor 方法来创建。这个线程池内部只有一个核心线程。并且最大线程数也是1 。它确保所有的任务都在同一个线程中按顺序执行。 SingleThreadExecutor 在于统一外界所有的任务到一个线程中。使得这些任务不需要处理同步的问题。

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

到这线程池的相关内容就讲完了。

猜你喜欢

转载自blog.csdn.net/mike_Cui_LS/article/details/81298748