Android线程池详解

来源:互联网 发布:linux目录权限 编辑:程序博客网 时间:2024/06/15 21:33

      最近在看OkHttp的源码,看着看着就看到了有线程池的地方,以前自己对这个东西就也就感到云里雾里的,所以

想把线程池的知识点和源码完整的看一篇

  1.线程池有什么用?

    a.重用线程池中存在的线程,避免因为线程的大量创建和销毁所带来的性能开销 ,完成一项任务的时间T=创建线

程的时间+在线程中执行任务的时间+线程销毁的时间, 而线程池的出现可以大大减少创建线程的时间和线程销毁的

时间,从而提高app的性能
    
    b.能有限的控制线程池的最大并发数,并且大量的线程互相抢占资源而导致的阻塞现象
    c.对线程池中线程进行有效的跟进,当我们不需要处理的时候可以将它shutdow掉
   并提供定时执行以及制定间隔循环执行, 

  

    2.ThreadPoolExcutor类详解

   threadPoolExcutor是线程池中最核心的类,所以要想弄懂线程池一定要弄懂这个类

   (1)首先来了解一下构造方法:

 

public ThreadPoolExecutor(int corePoolSize,                                int maximumPoolSize,                                long keepAliveTime,                                TimeUnit unit,                                BlockingQueue<Runnable> workQueue,                                ThreadFactory threadFactory,                                RejectedExecutionHandler handler);  
   接下来看看具体每个参数是什么意思?

   corePoolSize:核心线程池大小
   maximumPoolSize:最大线程池大小
   keepAliveTime:当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,
                       如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。
   unit:keepAliveTime时间单位
   workQueue:阻塞任务队列
   threadFactory:新建线程工厂
   handler:    当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来做拒绝处理

 

   (2)BlockingQueue详解:用于保存等待执行的线程任务的阻塞队列
        ArrayBlockingQueue(少):是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。      
        LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue  静态工厂方法Executors.newFixedThreadPool()使用了这个队列,如果不执行队列的大小,那么该

队列就是一个无界队列,无界队列的特征见PriorityBlockingQueue                       
        SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程         
       PriorityBlockingQueue(少):一个具有优先级的无限阻塞队列,使用无界队列将导致将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize和keepAliveTime的值也就无效了)
       
           
   (3)ThreadFactory作用:
       线程工厂,主要用来根据提交的新任务创建线程对象
      

 static class PicassoThreadFactory implements ThreadFactory {         @SuppressWarnings("NullableProblems")         public Thread newThread(Runnable r) {           return new Thread(r);        }     }     


 

   (4)ThreadPoolExcutor的执行步骤

      ①、当调用 execute() 方法添加一个任务时,线程池会做如下判断:
      a. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
      b. 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。
      c. 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这个任务;
      d. 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告诉调用者

“我不能再接受任务了”。
      ②、当一个线程完成任务时,它会从队列中取下一个任务来执行。

      ③、当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于

corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

这个过程说明,并不是先加入任务就一定会先执行。假设队列大小为 4,corePoolSize为2,maximumPoolSize为6,

那么当加入15个任务时,执行的顺序类似这样:首先执行任务 1、2,然后任务3~6被放入队列。这时候队列满了,

任务7、8、9、10 会被马上执行,而任务 11~15 则会抛出异常。最终顺序是:1、2、7、8、9、10、3、4、5、6。

当然这个过程是针对指定大小的ArrayBlockingQueue<Runnable>来说,如果无界队列,那么3-15的任务都会加到队列



  3.ThreadPoolExcutor的使用

  1)newFixedThreadPool(2)
      

     //指定了corePoolSize与maximumPoolSize为2     ExecutorServicer pool=Executors.newFixedThreadPool(2);     // 创建线程       Thread t1 = new MyThread();       Thread t2 = new MyThread();       Thread t3 = new MyThread();       // 将线程放入池中进行执行       pool.execute(t1);       pool.execute(t2);       pool.execute(t3);       // 关闭线程池       pool.shutdown();       private class MtThread extends Thread{...}  
     
      
   (2)单任务线程池newSingleThreadExecutor()
    
//只会有一个线程执行
     ExecutorService pool = Executors.newSingleThreadExecutor();

    
    
 (3).不限制大小,可变尺寸的线程池newCachedThreadPool()
   
//可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
    ExecutorService pool = Executors.newCachedThreadPool();

    
   
 
(4).延迟线程池,newScheduledThreadPool
    
       // 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。          ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);          // 创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口          Thread t1 = new MyThread();          Thread t2 = new MyThread();          Thread t3 = new MyThread();          // 将线程放入池中进行执行          pool.execute(t1);          // 使用延迟执行风格的方法          pool.schedule(t2, 1000, TimeUnit.MILLISECONDS);          pool.schedule(t3, 10, TimeUnit.MILLISECONDS);                   // 关闭线程池          pool.shutdown(); 

 
以上就是Executors类的四种静态创建线程池的方法,如果不能满足需求,还可以通过实例化ThreadPoolThread

来自定义线程池

 

  (5)ThreadPoolExecutor中几个重要的方法

  ① execute(Runnable r):向线程池添加一个线程任务
   ② Future<?> submit(Runnable r):向线程池添加一个线程任务,可以返回任务执行结果
   ③ shutdown():关闭线程池,不接收新的任务,关闭后,正在等待执行的任务不受任何影响,会正常执行,无返回值!
   ④ shutdownNow():关闭线程池,也不接收新的Task,并停止正等待执行的Task(也就是说,
   执行到一半的任务将正常执行下去),最终还会给你返回一个正在等待执行但线程池关闭却没有被执行的Task集合

  

   我们执行Runnable任务,他的run()方法是没有返回值的,那如果我们想要执行完一个任务,并且能够拿到一个返回值结果,那么应该怎么做呢?这个在我的另一篇博客已经将的很清楚了就不重复讲了Future和Callable详解

 

 

       

  

   


0 0
原创粉丝点击