【Android学习】线程的其他方式:AsyncTask、IntentService、HandlerThread、ThreadPoolExecutor

1,AsyncTask

1)概念

底层封装了线程和Handler。

AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程中更新UI。

AsyncTask实际上是串行执行的。

2)场景

方便子线程更新UI。耗时任务放在doInBackground中。

但不适合特别耗时的后台任务(建议使用线程池)。

3)应用

①AsyncTask的实例必须在主线程中创建,AsyncTask类必需在主线程中加载(第一次访问AsyncTask必须发生在主线程)。
②execute方法必须在主线程中调用。
③不要手动的调用onPreExecute,onPostExecute,doInBackground,onProgressUpdate这几个方法。
④一个AsyncTask对象只能被执行一次(即只能调用一次execute方法),否则会出现运行时异常。
⑤Android1.6之前,AsyncTask是传销执行任务的。
Android1.6之后可在线程池中并行处理任务。
Android3.0,为了避免AsyncTask所带来的并发错误,又采用一个线程来传销执行任务。

在3.0后续版本,通过AsyncTask的executeOnExecutor方法来并行的执行任务。(见- 5)问题 -①问题:无法进入doInBackground)

4)AsyncTask类

AsyncTask是一个抽象的泛型类。

①3个泛型参数

public abstract class AsyncTask<Params, Progress, Result>

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

对于不需要传递的参数,可用Void代替。

②4个核心方法

–onPreExecute()
在主线程中执行,在异步任务执行之前调用,用于做准备工作。

–doInBackground(Params…params)
在线程池中执行,用于执行异步任务。最后会返回计算结果给onPostExecute。
在这个方法中可以通过publishProgress方法更新任务进度,publishProgress会调用onProgressUpdate方法。

–onProgressUpdate(Progress…values)
在主线程中执行,当后台任务的执行进度改变时调用。

–onPostExecute(Result result)
在主线程中执行,在异步任务执行之后调用。
result为doInBackground的返回值。

–onCancelled()
在主线程中执行,异步任务取消时调用,这个适合onPostExecute不会被调用。

5)问题

①问题:无法进入doInBackground

在实际应用中,遇到很奇怪的问题,有时候AsyncTask无法进入doInBackground中。通过参阅http://blog.csdn.Net/mddy2001/article/details/17127065文章,解决了问题。要么不用AsyncTask,直接用Thread实现。要么将调用execute方法,改为调用task.executeOnExecutor(Executors.newCachedThreadPool());
原因是,execute()提交的任务,按先后顺序每次只运行一个。相当于同一时间,只有一个线程在再执行任务。而executeOnExecutor,让所有线程并发执行。

②demo

package com.luo.activity;  

import java.util.ArrayList;  
import java.util.Iterator;  
import java.util.List;  

import android.app.Activity;  
import android.os.AsyncTask;  
import android.os.Bundle;  

public class MainActivity extends Activity {  

    protected List<AsyncTask<Void, Void, Integer>> mAsyncTasks = new ArrayList<AsyncTask<Void, Void, Integer>>();  

    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        setAsync();  
    }  

    public void putAsyncTask(AsyncTask<Void, Void, Integer> asyncTask) {  
        mAsyncTasks.add(asyncTask.execute());  
    }  

    public void clearAsyncTask() {  
        Iterator<AsyncTask<Void, Void, Integer>> iterator = mAsyncTasks  
                .iterator();  
        while (iterator.hasNext()) {  
            AsyncTask<Void, Void, Integer> asyncTask = iterator.next();  
            if (asyncTask != null && !asyncTask.isCancelled()) {  
                asyncTask.cancel(true);  
            }  
        }  
        mAsyncTasks.clear();  
    }  

    private void setAsync() {  

        MainActivity.this.clearAsyncTask();  
        MainActivity.this.putAsyncTask(new AsyncTask<Void, Void, Integer>() {  

            @Override  
            protected void onPreExecute() {  
                super.onPreExecute();  

            }  

            @Override  
            protected Integer doInBackground(Void... params) {  

                    return 0;  

            }  

            @Override  
            protected void onPostExecute(Integer result) {  
                super.onPostExecute(result);  

                if (result == 0) {  

                }   
            }  
        });  
    }  

} 

6)AsyncTask工作原理

一个进程中的所有AsyncTask都在一个串行线程池中排队执行。

AsyncTask中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler)。
–SerialExecutor
用于任务排队,内部使用了一个队列,在一个同步方法内不断的将队列的任务加入到 threadPoolExecutor执行。
–THREAD_POOL_EXECUTOR
用于执行指定的任务。
–InternalHandler
用于将执行环境从线程池切换到主线程。

2,IntentService

3,HandlerThread

1)概念

继承了Thread。在它的内部可以使用Handler。

普通的Thread主要用于在run方法中执行一个耗时任务,而HandlerThread在内部创建了消息队列,外界需要通过Handler的消息方式来通知HandlerThread执行一个具体的任务。

2)实现

在run方法中通过Looper.prepare()创建消息队列,并通过Looper.loop()来开启消息循环,允许创建Handler。

run方法是一个无限循环,当不需要时需要退出:用quit或quitSafely方法。

3)场景

HandlerThread的一个具体使用场景是IntentService。

4,ThreadPoolExecutor 线程池

1)概念

一个线程池管理了一组工作线程,同时它还包括了一个用于放置等待执行任务的任务队列(阻塞队列)。
默认情况下,在创建了线程池后,线程池中的线程数为0。

Android中的线程池来自Java,主要通过Executor来派生特定类型的线程池。
创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer 类。

①Executor接口

Executor 是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable 类型,从字面意思可以理解,就是用来执行传进去的任务的;
真正的线程池的实现为ThreadPoolExecutor。

②Executors 类

③ExecutorService 接口

继承了Executor 接口,并声明了一些方法:submit、invokeAll、invokeAny 以及shutDown 等;

④AbstractExecutorService 抽象类

实现了ExecutorService 接口,基本实现了ExecutorService 中声明的所有方法;

⑤ThreadPoolExecutor 类

继承了类AbstractExecutorService。

2)优点

①重用线程池中的线程
避免因为线程的创建和销毁所带来的性能开销。
②有效控制线程池的最大并发数
避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
如果不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存。
③提高线程的可管理性。
使用线程池可以对线程进行统一的分配和监控,提供定时执行、指定间隔循环执行等功能。
④提高响应速度。
当任务到达时,任务可以不需要等到线程创建就可以立即执行。

3)ThreadPoolExecutor

ThreadPoolExecutor 的构造方法提供了一系列参数来配置线程池。

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

–corePoolSize
线程池的核心线程数。
默认情况下,核心线程在线程池中一直存活,即使处于闲置状态。
通过设置ThreadPoolExecutor的allowCoreThreadTimeOut属性为true,可以让闲置的核心线程在等待新任务时有超时策略。这个时间间隔由keepAliveTime所指定,超时后,线程终止。

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

–keepAliveTime
非核心线程闲置时的超时时长。
超过此时长,非核心线程就会被回收。
当ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true时,可作用于核心线程。

–unit
用于指定keepAliveTime参数的时间单位,这是一个枚举,常用的有TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分钟)。

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

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

–RejectedExecutionHandler handler
这个参数不常用。
当线程无法执行新任务时,可能原因:由于任务队列已满或者是无法成果执行任务。这个时候ThreadPoolExecutor会调用handler的rejectedExecution方法来通知调用者。

4)处理策略(核心线程corePoolSize 、任务队列workQueue、最大线程maximumPoolSize

当任务提交给线程池之后的处理策略如下:
1. 如果此时线程池中的数量小于corePoolSize(核心池的大小),即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务(也就是每来一个任务,就要创建一个线程来执行任务)。
2. 如果此时线程池中的数量大于等于corePoolSize,但是缓冲队列workQueue 未满,那么任务被放入缓冲队列,则该任务会等待空闲线程将其取出去执行。
3. 如果此时线程池中的数量大于等于corePoolSize , 缓冲队列workQueue 满,并且线程池中的数量小于maximumPoolSize(线程池最大线程数),建新的线程来处理被添加的任务。
4. 如果此时线程池中的数量大于等于corePoolSize , 缓冲队列workQueue 满,并且线程池中的数量等于maximumPoolSize,那么通过RejectedExecutionHandler 所指定的策略(任务拒绝策略)来处理此任务。也就是处理任务的优先级为: 核心线程corePoolSize 、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler 处理被拒绝的任务。
5. 特别注意,在corePoolSize 和maximumPoolSize 之间的线程数会被自动释放。当线程池中线程数量大于corePoolSize 时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize。这样,线程池可以动态的调整池中的线程数。

5)分类

AsyncTask对THREAD_POOL_EXECUTOR这个线程池进行了配置。

Android中最常见的4类线程池,都是直接或间接地通过配置ThreadPoolExecutor来实现自己的功能特性。

①FixedThreadPool

通过Executors的newFixedThreadPool方法来创建。
是一种线程数量固定的线程池,不回收空闲线程,除非线程池关闭了。

FixedThreadPool只有核心线程并且这些核心线程不会被回收。—>能够快速相应外界请求。

②CachedThreadPool

通过Executors的newCachedThreadPool方法来创建。
是一种线程数量不定的线程池,只有核心线程且最大线程数为Interger.MAX_VALUE(很大,相当于线程数可以任意大)。

当线程池中没有空闲线程时,线程池会创建新的线程来处理新任务。
对于空闲线程有超时机制,超过60s闲置线程会被回收。

场景:适合执行大量的耗时较少的任务。(大量线程处于空闲状态,会停止,几乎不占系统资源)

③ScheduledThreadPool

通过Executors的newScheduledThreadPool方法来创建。
核心线程数量固定,非核心线程数量无限制且闲置时会立即回收。

场景:用于执行定时任务和具有固定周期的重复任务。

④SingleThreadExecutor

通过Executors的newSingleThreadPool方法来创建。
线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行。

优点:统一所有的外界任务到一个线程中,这使得在这些任务之间不需要处理线程同步的问题。

6)风险

虽然线程池是构建多线程应用程序的强大机制,但使用它并不是没有风险的。
①线程池的大小。
多线程应用并非线程越多越好,需要根据系统运行的软硬件环境以及应用本身的特点决定线程池的大小。一般来说,如果代码结构合理的话,线程数目与CPU
数量相适合即可。如果线程运行时可能出现阻塞现象,可相应增加池的大小;如有必要可采用自适应算法来动态调整线程池的大小,以提高CPU 的有效利用率和系统的整体性能。
②并发错误。
多线程应用要特别注意并发错误,要从逻辑上保证程序的正确性,注意避免死锁现象的发生。
③线程泄漏。
这是线程池应用中一个严重的问题,当任务执行完毕而线程没能返回池中就会发生线程泄漏现象。

7,性能优化

1)避免创建过多的对象
2)不要过多使用枚举
枚举占用的内存空间比整型大。
3)常量请使用static final来修饰
4)使用一些Android特有的有更好性能的数据结构
如SparseArray、Pair等。
5)适当使用软引用和弱引用
6)采用内存缓存和磁盘缓存
7)尽量采用静态内部类
避免由于内部类OOM。

猜你喜欢

转载自blog.csdn.net/SunshineTan/article/details/78720122
今日推荐