Android线程池详解

25 篇文章 0 订阅
14 篇文章 0 订阅

android中经常会用到多线程,因为安卓中如果5S中没有响应就会出现ANR,因此耗时的任务必须放在子线程中使用。这就涉及到UI线程(main线程)和子线程的交互。其二者之间的交互涉及到Handler,Looper,message。
每次创建线程都有系统资源的开销,不可能无限的创建线程,线程过多最终会带来系统的反应速度下降,因此就必须控制线程数量,控制线程的最好办法就是设计线程池,让线程可以重复利用。
下面将会通过三种方式来创建安卓的线程池。
1、通过静态列表缓存10个子线程,创建一个主线程,每个线程中创建唯一Handler,放在子线程的代码。
2、自定义线程池中还是设计一个主线程(UI线程),同时缓存10个子线程,当然这里创建的子线程采用android提供的HandlerThread来代替上面自定义的线程。采用HandlerThread会更加简便,后续会通过代码进行说明。
3、通过系统提供的ThreadPoolExecutor来设计线程池。

一、自定义Thread来创建线程池。

public class ThreadExtractors {
    public class WorkThead extends Thread {
        private boolean mIsQuiting = false;
        private boolean mIsLoopEnd = false;
        Handler myHandler = null;
        private final String mThreadId;
        public WorkThead(String threadId){
            mThreadId = threadId;
        }
        @Override
        public void run() {
            //Looper.prepare() 和 Looper.loop()成对存在,创建Handler必须指定looper否则会抛异常
            //需要通过synchronized来保证线程安全
            Looper.prepare();
            synchronized (this){
                myHandler = new Handler();
                //myHandler创建成功通知所有等待的线程
                notifyAll();
            }
            Looper.loop();
        }

        public Handler getHandler(){
            synchronized (this) {
                while (null == myHandler) {
                    try {
                        //如果myHandler没创建成功,就需要等待,直到myHandler被创建成功
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            return myHandler;
        }

        public void  quit(){
            //退出时需要清除句柄,避免内存泄露
            if (mIsQuiting)
                return;
            mIsQuiting = true;
            if (null != myHandler){
                //清除message队列及其回调
                myHandler.removeCallbacksAndMessages(null);
                myHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        Looper.myLooper().quit();
                        mIsLoopEnd = true;
                    }
                });
            }
        }

        public boolean isQuiting(){
            return mIsQuiting;
        }

        public boolean isLoopEnd(){
            return this.mIsLoopEnd;
        }

    }

    //最大线程数量
    private static final int MAX_TREAD_COUNT = 10;
    private static final int minThreadCount = 5;
    //当前线程数
    private static int currentThreadCount = 0;
    //保留的线程数
    private static int RETENTION_THREAD_COUNT = 10;

    private WorkThead[] retentionThreads;

    private static ThreadExtractors THREAD_POOL = null;
    private static Handler mainHandler = null;

    private ThreadExtractors(){
        mainHandler = new Handler(Looper.getMainLooper());
        //子线程数目为10个
        retentionThreads = new WorkThead[RETENTION_THREAD_COUNT];
        for(int i = 0; i < RETENTION_THREAD_COUNT; i ++){
            retentionThreads[i] = newThread();
        }
    }

    private WorkThead newThread(){
        String threadId = UUID.randomUUID().toString();
        WorkThead workThead = new WorkThead(threadId);
        workThead.start();
        return workThead;
    }

    public void OnDestroy(){
        if (null == retentionThreads)
            return;

        for (int i = 0, length = retentionThreads.length; i < length; i++){
            retentionThreads[i].quit();
        }
        mainHandler.removeCallbacksAndMessages(null);
        retentionThreads = null;
        THREAD_POOL = null;
    }

    public static void extractMainThread(Runnable runnable){
        THREAD_POOL.extractMainThread(runnable, 0);
    }

    public synchronized static void extractMainThread(Runnable runnable, int delayTime){
        if (null == THREAD_POOL)
            THREAD_POOL = new ThreadExtractors();
        THREAD_POOL.mainHandler.postDelayed(runnable, delayTime);
    }

    public static void extractInThread(Runnable runnable){
        THREAD_POOL.extractThread(runnable, 0);
    }

    public synchronized static void extractThread(Runnable runnable, int delayTime){
        if (null == THREAD_POOL)
            THREAD_POOL = new ThreadExtractors();
        currentThreadCount++;
        if(currentThreadCount >= MAX_TREAD_COUNT)
            currentThreadCount = 0;
        THREAD_POOL.retentionThreads[currentThreadCount].getHandler().postDelayed(runnable, delayTime);
    }

    private HandlerThread newHandlerThread(){
        HandlerThread handlerThread = new CusmHandlerThread(UUID.randomUUID().toString());
        handlerThread.start();
        return handlerThread;
    }

    /** HandlerThread内部中已经封装好了线程安全,因此不必担心多线程问题
     *
     */
    public class CusmHandlerThread extends  HandlerThread{
        private Handler mMyHandler = null;
        public CusmHandlerThread(String name) {
            super(name);
            mMyHandler = new Handler(this.getLooper());
        }

        public void setHandler(Handler handler){
            mMyHandler = handler;
        }

        public Handler getHandler(){
            return this.mMyHandler;
        }
    }
}

对以上线程池代码进行讲解:
1、从66行 -75行可以看出,本线程池定义了1个主线程,10个子线程。
2、110行 extractMainThread(Runnable runnable, int delayTime)方法中加上同步关键字synchronized,是为了避免多线程安全问题。
3、需要注意子线程类WorkThead的定义,本线程只会创建Handler,创建Handler的时候请注意,每个Handler必须制定唯一的looper,因此如果需要自己创建非主线程的Handler,则必须将其创建放在Looper.prepare() 和 Looper.loop()之间,直到创建完成为止。
4、注意23行的getHandler()加了synchronized,是因为避免获取的时候其还没创建成功。
5、注意37行的quit()函数,退出的时候需要清理资源,避免内存泄露。

有没发现自定义线程WorkThead的实现相对来说比较繁琐,有没有更简便的方法呢?当然是有的,这就需要用到安卓封装的HandlerThread。
1、请查看129行~152行的HandlerThread定义,其功能完全可以代替WorkThead的功能,并且不用理会线程安全及其清理工作, HandlerThread内部会自动的去处理。当然HandlerThread内部也实现了线程安全问题。

下面看看以上设计的线程怎样调用:

        ThreadExtractors.extractMainThread(() ->{
            System.out.println("这里的代码是在UI线程执行的,本消息会在主线程的Handler的消息队列中排队处理");
        });

        ThreadExtractors.extractInThread(() -> {
            System.out.println("这里的代码是在非主线程中执行的,本消息会在非主线程的Handler的消息队列中排队处理");
        });

本代码中采用了JAVA8中的新特性拉姆表达式,不熟悉的谷歌一下。
需要在主线程或者子线程执行,直接套用以上方法即可,当然每个线程post的messge会在当前线程的handler中进行排队,并且是顺序执行的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值