Android 多线程

    xiaoxiao2021-04-12  33

    简介

    1. 线程分类

    主线程(UI线程) : 处理和界面相关的事情.子线程 : 处理耗时操作.

    Android中规定, 只有在主线程中可以进行UI操作, 但是同时, 主线程中不能进行耗时操作,否则会产生ANR,因此耗时操作必须放到子线程中进行处理.

    2. Android中多线程技术

    ThreadAsyncTask : 底层用到了线程池.

    封装了线程池和Handler, 主要使用场景是在子线程中更新UI.

    IntentService : 底层使用Thread 内部采用HandlerThread来执行任务,当任务执行完成后IntentService就会自动退出. 从任务执行的角度看,IntentService很像是一个后台线程,其实他是一个Service,正是因为他是服务所以不容易被系统杀死从而保证任务的正常进行.HandlerThread : 底层使用Thread 具有消息循环的线程, 内部可以使用Handler. 注意一 : 从Android 3.0 开始如果在子线程中进行网络操作就会产生NetworkOnMainThreadException

    AsyncTask

    AsyncTask 是一个轻量级异步任务类, AsyncTask 封装了Thread和Handler, 通过AsyncTask可以更加方便地执行后台任务以及在主线程中访问UI, 但是AsyncTask并不适合进行特别耗时的后台任务,对于特别耗时的后台任务来说建议使用线程池.

    1. AsyncTask 简单使用

    创建自定义任务类,继承自AsyncTask. /** * 自定义 AsyncTask 类. * P1 : 输入参数类型 * P2 : 进度类型 * P3 : 返回值类型 */ private class DownloadTask extends AsyncTask<Integer, Integer, Integer> { /** * 1. 主线程 * 2. 执行任务之前执行, 常用来初始化任务,显示UI等. */ @Override protected void onPreExecute() { Log.d(TAG, "onPreExecute: " + Thread.currentThread().getId()); } /** * 1. 子线程 * 2. onPreExecute执行后会立即执行这个方法.任务逻辑就是在这个方法中进行的. * 3. 可以通过 publicProgress(Progress...) 将任务进度传递到onProgressUpdate. * 4. 任务结果通过返回值返回, return.传递到onPostExecute. * @param params P2 * @return P3 */ @Override protected Integer doInBackground(Integer... params) { Log.d(TAG, "doInBackground: " + Thread.currentThread().getId()); for (int i = 0; i < params[0]; i++) { // 检查任务是否被取消 if (isCancelled()) { return -1; } publishProgress(i); } return 0; } /** * 1. 主线程 * 2. publicProgress被调用后会将进度传递到这个方法中.可以在这进行UI更新操作. * @param values 任务进度. */ @Override protected void onProgressUpdate(Integer... values) { Log.d(TAG, "onProgressUpdate: " + Thread.currentThread().getId()); Log.d(TAG, "onProgressUpdate: " + values[0]); } /** * 任务执行结束后,就会进入这个回调. * @param integer 任务结果 */ @Override protected void onPostExecute(Integer integer) { Log.d(TAG, "onPostExecute: " + Thread.currentThread().getId()); Log.d(TAG, "onPostExecute: " + integer); } /** * 任务取消后调用 * @param integer doInBackground 返回值. */ @Override protected void onCancelled(Integer integer) { super.onCancelled(integer); } } 启动任务 // 创建并启动任务 new DownloadTask().execute(5); 输出 // D/MainActivity: onPreExecute: 1 // D/MainActivity: doInBackground: 161 // D/MainActivity: onProgressUpdate: 1 // 线程id D/MainActivity: onProgressUpdate: 0 // 进度 D/MainActivity: onProgressUpdate: 1 D/MainActivity: onProgressUpdate: 1 // 进度 D/MainActivity: onProgressUpdate: 1 D/MainActivity: onProgressUpdate: 2 // 进度 D/MainActivity: onProgressUpdate: 1 D/MainActivity: onProgressUpdate: 3 // 进度 D/MainActivity: onProgressUpdate: 1 D/MainActivity: onProgressUpdate: 4 // 进度 // D/MainActivity: onPostExecute: 1 // 线程id D/MainActivity: onPostExecute: 0 // 结果

    代码中已经有了详细的注释,不进行过多解释,通过Log也证实了.这里说下取消任务

    AsyncTask 提供了 cancel(boolean) 取消任务. 1. 参数含义:是否立即中断线程执行,true表示立即终止线程,false表示允许任务完成. 2. 调用了cancel() 后onCancelled() 回调会被执行(UI线程), onPostExecute() 不会被执行了. 3. 调用后 isCancelled 会返回 true. 4. 建议onInBackground() 中检查 isCancelled 以便尽快结束任务.

    2. AsyncTask使用注意

    AsyncTask这个类必须在主线程中进行加载. 在Android 4.1 以后系统自动完成这一过程.Android 6.0 以后可以在子线程中加载AsyncTask必须在主线程中创建.execute() 必须在UI线程中进行调用.不要在直接调用 onPreExecute(), onPostExecute(), onInBackground() ,onProgressUpdate() 等方法.一个AsyncTask 对象 只能调用一次execute方法,如果执行多个任务就创建多个任务.Android3.0 以后 ,AsyncTask用一个线程串行执行任务.

    3. AsyncTask的工作原理

    首先分析execute() 方法.由于它是直接调用了executeOnExecutor()因此主要分析一下后者 @MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { // 调用 : executeOnExecutor // sDefaultExecutor 是一个串行的线程池Executor. return executeOnExecutor(sDefaultExecutor, params); } @MainThread public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) { // 检查状态, 此处可用证明为什么一个AsyncTask对象只可以调用一次execute方法. 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, 证实了onPreExecute他回调最先执行. // onPreExecute在主线程中执行, 这也是AsyncTask的execute()必须在主线程调用的一个的原因. onPreExecute(); // 保存参数 mWorker.mParams = params; // 执行线程 // mFuture 是一个Runnable. exec.execute(mFuture); return this; }

    代码中已经有了详解的注释这里就不过多的解释了. 1. @MainThread 注解说明这两个方法都必须在主线程中执行,因此execute()方法必须在主线程中调用. 2. if (mStatus != Status.PENDING){...}中的判断说明了,一个AsyncTask对象只可以调用一次execute() 方法. 3. sDefaultExecutor是一个串行的线程池主要用于任务排队,并不负责任务的实际执行,源码分析如下

    // sDefaultExecutor 定义 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; // SERIAL_EXECUTOR : 用于任务排队. public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); // SerialExecutor 串行线程池 private static class SerialExecutor implements Executor { final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); Runnable mActive; // 执行Runnable public synchronized void execute(final Runnable r) { mTasks.offer(new Runnable() { public void run() { try { r.run(); } finally { // 调度下一个. scheduleNext(); } } }); if (mActive == null) { scheduleNext(); } } // 启动下一个Runnable protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) != null) { // 执行任务. THREAD_POOL_EXECUTOR.execute(mActive); } } } // mFuture是一个Runnable private final FutureTask<Result> mFuture; THREAD_POOL_EXECUTOR 也是一个线程池,负责任务的执行. // 定义 public static final Executor THREAD_POOL_EXECUTOR; // 静态加载线程池. static { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); threadPoolExecutor.allowCoreThreadTimeOut(true); THREAD_POOL_EXECUTOR = threadPoolExecutor; } sHandler 负责线程环境的切换. // 7.0 代码 // 负责线程切换 private static InternalHandler sHandler; // 获取Handler private static Handler getHandler() { synchronized (AsyncTask.class) { if (sHandler == null) { sHandler = new InternalHandler(); } return sHandler; } } // 该类负责实现线程的切换 private static class InternalHandler extends Handler { // 7.0 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; } } } // 任务结束函数 private void finish(Result result) { if (isCancelled()) { // 任务被取消 onCancelled(result); } else { // 任务正常完成 onPostExecute(result); } mStatus = Status.FINISHED; }

    Android 5.0 代码

    // 静态加载. private static final InternalHandler sHandler = new InternalHandler(); private static class InternalHandler extends Handler { @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对象,Android 6.0 以前,为了能够将执行环境切换到主线程,这就要求sHandler这个对象必须在主线程中进行创建.Android 6.0 以后 sHandler 使用了懒加载,因此,在子线程中也可以进行加载AsyncTask.同时在ActivityThread的main() 方法中也去掉了AsyncTask.init(); 也证实了这一点.

    5 总结

    AsyncTask 本质上就是封装了线程池和HandlerAndroid 6.0 以后可以在子线程中加载AsyncTask.

    HandlerThread

    HandlerThread 继承了Thread ,添加加了Looper, 实现方式如下: - 通过Looper.prepare() 来创建消息队列. - 通过Looper.loop() 来开启消息循环.

    @Override public void run() { mTid = Process.myTid(); // 创建消息队列. Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); // 子类实现. onLooperPrepared(); // 开启运行循环. Looper.loop(); mTid = -1; } 普通的Thread主要用于进行耗时操作.HandlerThread内部常见了消息队列,外界需要通过Handler的消息方式通知HandlerThread来执行一个任务.由于HandlerThread的run() 是一个无限循环(Looper.loop()),因此当明确不需要时需要主动调用,quit() 或者 quitSafely() 来终止线程的执行.

    关于HandlerThread的使用方式参考IntentService源码

    IntentService

    1. 简介

    IntentService是一种特殊的服务, 他继承自Service并且他是一个抽象类. IntentService用于后台耗时任务, 任务执行结束后自动停止.由于它是一个服务因此有着比普通Thread更高的优先级,不容易被系统杀死.因此他适合执行一些优先级较高的后台任务.

    2. 工作原理

    IntentService 其实就是封装了 Handler 和 HandlerThread从下面的源码分析中可以看出来. // 1. Handler private volatile ServiceHandler mServiceHandler; // 定义Handler private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { // onHandleIntent 这个方法是子类来实现的. onHandleIntent((Intent)msg.obj); // 停止当前服务. stopSelf(msg.arg1); } } // 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. HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); // 保存Looper mServiceLooper = thread.getLooper(); // 创建Handler , 并且将创建的Handler 和 HandlerThread 的Looper绑定在一起. mServiceHandler = new ServiceHandler(mServiceLooper); } mServiceHandler就是一个Handler , 并且他的handleMessage处理逻辑也十分简单,首先调用onHandleIntent将结果返给子类处理. 然后调用stopSelf() 来停止服务.在onCreate() 方法中创建了一个HandlerThread 并且将Handler和他绑定.

    IntentService工作原理如下:

    在onCreate() 中创建Handler和HandlerThread.并绑定.在onStart() 中通过Handler 发送消息.在 Handler中处理调用onHandleIntent()进行耗时操作.数据通过Intent进行传输.任务执行结束后,后停止服务.在onDestory() 中退出HandlerThread.

    3. 使用

    // 定义 public class LocalIntentService extends IntentService{ /** * 构造方法 */ public LocalIntentService() { super("123"); } @Override protected void onHandleIntent(Intent intent) { String action = intent.getStringExtra("task_action"); Log.d(TAG, "onHandleIntent: " + action); // 延时 SystemClock.sleep(500); } @Override public void onDestroy() { Log.d(TAG, "onDestroy: 服务结束"); super.onDestroy(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand: "); return super.onStartCommand(intent, flags, startId); } } Intent intent = new Intent(this, LocalIntentService.class); intent.putExtra("task_action", "任务 1"); startService(intent); // 任务二 intent.putExtra("task_action", "任务 2"); startService(intent); SystemClock.sleep(2000); // 任务三 intent.putExtra("task_action", "任务 3"); startService(intent);

    运行代码发现上述三个任务串行执行,最后会停止服务.

    Android中的线程池

    Android线程池的概念源自于Java的Executor, Executor 是一个接口,ThreadPoolExecutor 是他的实现类.ThreadPoolExecutor提过了一系列的参数来配置线程池. Android中的线程池主要分为四类.后面会详细介绍.

    1. 线程池的好处

    重用线程池中的线程, 避免线程的创建和销毁带来的性能开销.能有效的控制线程池的最大并发数, 避免大量线程之间因为抢夺子系统资源而导致的阻塞现象.能够对象成进行简单的管理, 并提供定时执行以及定时间隔循环执行的功能.

    2. ThreadPoolExecutor

    构造方法 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory); corePoolSize : 线程池的核心线程数,默认情况下核心线程会在线程池中一直存活.如果ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设置为true, 那么闲置的核心线程会有超时策略,超时时间由keepAliveTime指定.maximumPoolSize : 线程池最大的线程数.达到最大值后新任务会阻塞.keepAliveTime : 超时时间.unit : 超时单位,MILLISECONDS(毫秒),SECONDS(秒), MINUTES(分钟)workQueue: 线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这里.threadFactory : 线程工厂, 为线程池提供创建新线程的功能.

    除了上面的主要参数以外, ThreadPoolExecuteor还有一个不常用的参数RejectedExecutionHandler handler. 当线程池无法执行新的任务时会调用handler 的rejectedExeception方法来通知调用者, 默认情况下 rejectedExecution 方法会抛出一个rejectedExecutionException异常,具体的可以查看API文档.

    ThreadPoolExecutor 执行时遵循以下规则:

    如果线程池中的线程为达到核心线程的数量, 那么会直接启动一个核心线程来执行任务.如果线程池中的线程数量已经达到或者超过核心线程数量, 那么任务会插入任务队列中进行排队.如果过步骤2中无法将任务插入到任务队列,一般是队列已满,这个时候如果线程数量没有达到线程池的最大数量限制,那么会立即启动一个非核心线程来执行任务.如果步骤3中线程数量已经到了线程池规定的最大值, 那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution() 方法来通知调用者.

    AsyncTask中的线程池配置

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); // We want at least 2 threads and at most 4 threads in the core pool, // preferring to have 1 less than the CPU count to avoid saturating // the CPU with background work private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final int KEEP_ALIVE_SECONDS = 30; static { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); threadPoolExecutor.allowCoreThreadTimeOut(true); THREAD_POOL_EXECUTOR = threadPoolExecutor; }

    3. Android中的四种线程池.

    Android中常用有四种线程池,他们都是直接或者间接配置ThreadPoolExecutor 来实现自己的功能的.

    3.1 FixedThreadPool

    通过Executors 的newFixedThreadPool 方法来创建.线程数量固定.线程处于空闲状态时, 线程也不会被回收,除非线程池关闭了.正是这个原因所以它能够更加快速第响应外界请求.当所有的线程都处于活动状态时, 新任务处于等待状态, 直到有线程空闲.没有超时机制没有任务熟练限制.

    newFixedThreadPool方法源码

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

    3.2 CachedThreadPool

    通过Executors的newCachedThreadPool 来创建, 它是一种线程数量不定的线程池,他只有非核心线程并且最大线程数为integer.MAX_VALUE.当线程池中的活动都处于活动状态时,会直接创建新的线程来处理任务.否则就利用空闲线程来处理任务.超时时间是 60s ,线程闲置60s就会被回收.这种线程适合大量的耗时较小的任务.

    newCachedThreadPool源码

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

    3.3 ScheduledThreadPool

    通过Executors的newScheduledThreadPool方法来创建.核心线程数是固定的,非核心线程数没有限制.当非核心线程一旦闲置就会被立即回收.ScheduledThreadPool主要用来执行定时任务和具有固定周期的重复任务.

    newScheduledThreadPool源码

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

    3.4 SingleThreadPool

    通过Executors的newSingleThreadExecutor方法创建.只有一个核心线程,他可以确保所有的任务都在同一个线程中顺序执行.他可以统一所有的外界任务到一个线程中,这样可以避免线程同步问题.

    newSingleThreadExecutor源码

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

    4. 四种常用线程池的用法

    private void threadPoolTest() { // 创建任务 Runnable command = new Runnable() { @Override public void run() { Log.d(TAG, "run: " + Thread.currentThread().getId()); SystemClock.sleep(2000); } }; // FixedThreadPool ExecutorService fixed = Executors.newFixedThreadPool(4); fixed.execute(command); // Cached ExecutorService cached = Executors.newCachedThreadPool(); cached.execute(command); // Scheduled ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(4); // 2000 ms 后执行 scheduled.schedule(command, 2000, TimeUnit.MILLISECONDS); // 延时 10 后每隔 1000ms 执行一次 scheduled.scheduleAtFixedRate(command, 10, 1000, TimeUnit.MICROSECONDS); // Single ExecutorService single = Executors.newSingleThreadExecutor(); single.execute(command); }
    转载请注明原文地址: https://ju.6miu.com/read-667725.html

    最新回复(0)