Android 的线程和线程池

2 篇文章 0 订阅
1 篇文章 0 订阅

  不知不觉在Android这个行业行走了有三年时间了。过程坎坷而充沛,想想一路走来自己的经验丰富了,但这些随着时间的流失,我所记忆的知识点有时在慢慢的淡忘,所以今年起开通了CSDN博客,记录自己行走的点点滴滴...

  线程在Android中是一个很重要的概念,从用处来说线程可以分为主线程和子线程。主线程处理和界面有关的事情,子线程则用于执行耗时操作。我们都知道Android的特性,如果在主线程中执行耗时操作就会导致ANR,因此耗时操作必须放在子线程中去执行。除了Thread本身以外,扮演线程角色的还有很多,比如AsyncTask和IntentService,同时HandlerThread 也是一种特殊的线程,但是它们的本质仍然是传统的线程。对于AsyncTask来说,它的底层用到了线程池,对于IntentService和HandlerThread来说,它们的底层则直接使用了线程。

  以上说的这些虽然都是线程,但是仍然具有不同的特征和使用场景。AsyncTask封装了线程和Handler,它主要是为了在子线程中更新UI。HandlerThread是一种具有消息循环的线程,在它的内部可以使用Handler。IntentService是一个服务,系统对其进行了封装使其更方便的执行后台任务,IntentService内部采用HandlerThread来执行任务,当任务执行完毕后IntentService会自动退出。IntentService的优点:它是一种服务,不容易被系统杀死从而保证任务的执行。

  在操作系统中,线程是操作系统调度的最小单元,同时线程又是一种受限制的系统资源,线程不可能一直产生,线程的创建和销毁都有一定的开销。如何做到高效呢,--采用线程池。一个线程池中会缓存一定数量的线程,通过线程池就可以避免因为频繁创建和销毁线程所带来的系统开销。Android线程池来源于Java,主要通过Executor来派生特定类型的线程池,不同种类的线程池又具有各自的特性。

 一:Android中的线程形态

 1:AsyncTask
AsyncTask是一种轻量级的异步任务类,他可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。从实现上来说,AsyncTask封装了Thread和Handler,通过AysncTask可以更加方便执行后台任务以及在主线程中访问UI,但是AsyncTask并不合适进行特别耗时的后台任务,对于特别耗时的任务来说,还是建议使用线程池。关于AsyncTask的使用我在之前的博客中有提到,感兴趣的可以看下:Android之AsyncTask学习

2:HandlerThread
HandlerThread 继承自Thread,内部封装了Looper。它的实现就是在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环。
首先Handler和HandlerThread的主要区别是:Handler与Activity在同一个线程中,HandlerThread与Activity不在同一个线程,而是别外新的线程中(Handler中不能做耗时的操作)。
更具体点就是说普通的Handler主要用于在run方法中执行一个耗时任务,而HandlerThread在内部创建了消息队列,外界需要通过Handler的消息方式来通知HandlerThread执行一个具体的任务。特别注意的是:HandlerThread的run方法是一个无限循环,因此当不需要使用的时候一定要通过quit或者quitSafely方法来终止线程的执行。否则长期运行容易产生内存泄漏!

3:IntentService
IntentService是一种特殊的Service,他继承了Service并且它是一个抽象类,因此必须创建它的子类才能使用。IntentService可用于执行后台耗时的任务,当任务执行后它会自动停止,同时它是服务所以它的优先级比单纯的线程高很多,这样来说,IntentService比较适合执行一些优先级高的后台任务,因为它的优先级高不容易被杀死。
IntentService:异步处理服务,新开一个线程:handlerThread在线程中发消息,然后接受处理完成后,会清理线程,并且关掉服务。

IntentService有以下特点:
(1)它创建了一个独立的工作线程来处理所有的通过onStartCommand()传递给服务的intents。
(2)创建了一个工作队列,来逐个发送intent给onHandleIntent()。
(3)需要主动调用stopSelft()来结束服务。因为,在所有的intent被处理完后,系统会自动关闭服务。
(4)默认实现的onBind()返回null
(5)默认实现的onStartCommand()的目的是将intent插入到工作队列中
继承IntentService的类至少要实现两个函数:构造函数和onHandleIntent()函数。要覆盖IntentService的其它函数时,注意要通过super调用父类的对应的函数。

  当IntentService第一次启动以后,onCreate方法会创建一个HandlerThread,然后利用它Loop来创建一个mServiceHandler,这样通过mServiceHandler发送的消息都会交给HandlerThread来处理。每次启动IntentService都会调用它的startCommand方法,startCommand调用onStart。

<span style="font-size:14px;"> @Override  
    public void onStart(Intent intent, int startId) {  
        Message msg = mServiceHandler.obtainMessage();  
        msg.arg1 = startId;  
        msg.obj = intent;  
        mServiceHandler.sendMessage(msg);  
    }  </span>
可以看到IntentService通过mServiceHandler发送了一条消息,这个消息会被HandlerThread处理,mServiceHandler收到消息后会将intent传给onHandleIntent方法处理。注意这个intent对象和外界startService传递的intent是一样的,通过这个intent可以解析出外界传递给IntetnService的参数,通过这些参数就可以区分具体的后台任务,onHandleIntent就可以进行针对性的处理。执行完毕后,IntentService会通过stopSelf(startId)来尝试停止服务,如果此时还有别的消息未处理,stopSelf(startId)会等待全部处理完毕再关闭。(通过判断最近启动服务次数和startId是否相等)

<span style="font-size:14px;">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);  
        }  
    }  </span>

IntentService中的onHandleIntent方法需要我们继承之后自己实现,它可以区分intent中的参数来执行具体任务。每启动一次IntentService就等同于内部向HandlerThread发送一次请求,而Handler中的Looper是顺序执行任务的,所以intentService也是顺序执行后台任务的。

  二:Android中的线程池
先说一下线程池的优点:
(1) 复用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。
(2) 能有效控制线程池的最大并发数,避免大量的线程之间因相互抢占系统资源而导致的阻塞现象。
(3) 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。
在Android中当同时并发多个网络线程时,引入线程池技术会极大地提高APP的性能。Android中的线程池的概念来源Java中的Executor是一个接口,真正的线程池的实现为ThreadPoolExecutor。ThreadPoolExecutor提供了一系列参数来配置线程池,通过不同的参数可以创建不同的线程池。

详细的介绍可以参考后面转载的文章:Android 线程池的使用

线程池例子:
1)JDK自身带有线程池的实现类ThreadPoolExecutor
2)下面是一个模拟ThreadPoolExecutor的例子,以加深对原理的理解

public final class ThreadPool {
     // 线程池中默认线程的个数为5
     private static int worker_num = 5;
     // 工作线程
     private WorkThread[] workThreads;
    
     // 任务队列,作为一个缓冲,List线程不安全
     private List<Runnable> taskQueue = new LinkedList<Runnable>();

     private static ThreadPool threadPool;

     // 创建具有默认线程个数的线程池
     private ThreadPool() {
          this(5);
     }

     // 创建线程池,worker_num为线程池中工作线程的个数
     private ThreadPool(int worker_num) {
          ThreadPool.worker_num = worker_num;
          workThreads = new WorkThread[worker_num];
          for (int i = 0; i < worker_num; i++) {
               workThreads[i] = new WorkThread();
               workThreads[i].start();// 开启线程池中的线程
          }
     }

     // 单态模式,获得一个默认线程个数的线程池
     public static ThreadPool getThreadPool() {
          return getThreadPool(ThreadPool.worker_num);
     }

     // 单态模式,获得一个指定线程个数的线程池,worker_num(>0)为线程池中工作线程的个数
     // worker_num<=0创建默认的工作线程个数
     public static ThreadPool getThreadPool(int worker_num1) {
          if (threadPool == null)
               threadPool = new ThreadPool(worker_num1);
          return threadPool;
     }

     // 执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定
     public void addTask(Runnable task) {
          synchronized (taskQueue) {
               taskQueue.add(task);
               taskQueue. notifyAll();
          }
     }

     // 销毁线程池,该方法保证在所有任务都完成的情况下才销毁所有线程,否则等待任务完成才销毁
     public void destroy() {
          while (!taskQueue.isEmpty()) {// 如果还有任务没执行完成,就先睡会吧
               try {
                    Thread.sleep(10);
               } catch (InterruptedException e) {
                    e.printStackTrace();
               }
          }
          // 工作线程停止工作,且置为null
          for (int i = 0; i < worker_num; i++) {
               workThreads[i].stopWorker();
               workThreads[i] = null;
          }
          threadPool=null;
          taskQueue.clear();// 清空任务队列
     }

     /**
      * 内部类,工作线程
      */
     private class WorkThread extends Thread {
          // 该工作线程是否有效,用于结束该工作线程
          private boolean isRunning = true;

          /*
           * 关键所在啊,如果任务队列不空,则取出任务执行,若任务队列空,则等待
           */
          @Override
          public void run() {
               Runnable r = null;
               while (isRunning) {// 注意,若线程无效则自然结束run方法,该线程就没用了
                    synchronized (taskQueue) {
                         while (isRunning && taskQueue.isEmpty()) {// 队列为空
                              try {
                                   taskQueue.wait(20);
                              } catch (InterruptedException e) {
                                   e.printStackTrace();
                              }
                         }
                         if (!taskQueue.isEmpty())
                              r = taskQueue.remove(0);// 取出任务
                    }
                    if (r != null) {
                         r.run();// 执行任务
                    }
                    r = null;
               }
          }

          // 停止工作,让该线程自然执行完run方法,自然结束
          public void stopWorker() {
               isRunning = false;
          }
     }
}

                                                                                                                                                                                         ------------------          参考文献:《Android开发艺术探索》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值