美文网首页
线程池初探

线程池初探

作者: 小小小小小粽子 | 来源:发表于2018-08-02 22:07 被阅读6次

    日常开发中,我们经常会有一些任务需要在额外的线程中执行。尤其是在Android中,我们不能阻塞主线程,我们更加需要异步地去完成某些任务。最原始的方式便是new一个Thread,然后通过回调的方式处理结果。

    当然这种方式并不好,因为线程的创建/销毁,以及切换上下文都会带来系统开销,频繁地重复这个过程很大程度上会影响处理效率。那如果能复用之前创建的线程,是不是就能避免线程创建/销毁带来的开销了呢?

    我们聪明的前辈们考虑到了这一点,给我们带来了方便的线程池。线程池的应用范围很广泛,比如我们熟悉的AsyncTask就是对线程池的封装。

    在深入了解线程池的种类之前,我们先来看一下初始化线程池需要的参数。

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

    我们稍后再来解释各个参数代表的意思,先看看其他几个构造函数。

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

    其中corePoolSize表示的是核心线程数。在线程池新建线程的时候,如果当前线程数量<
    corePoolSize,则新建的是核心线程,否则就是非核心线程。核心线程默认即使啥也不干也会保留在线程池中,除非我们设置allowCoreThreadTimeOut为true,这样的话闲置的核心线程在超过keepAliveTime这个时间还没有干活的话就会被回收。那keepAliveTime的作用也解释清楚了,TimeUnit用来作为描述它的单位。而maximumPoolSize则表示线程池的线程总数,因为我们不仅有核心线程,还有非核心线程。ThreaFactory是创建线程使用的工厂类,一般我们用不到。RejectedExecutionHandler 是处理问题的策略,系统给我们提供了四个实现。ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常, ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常, ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)。 ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。

    接下来就是比较重要的一个参数了,BlockingQueue。这是线程池的任务队列,当核心线程都在处理任务时,新添加的任务会被添加到队列中等待处理,如果队列满了,就会新建非核心线程来执行任务。我们来看一下常用的几个队列的实现:

    1. SynchronousQueue
      这个queue不会保留任何任务,接收到新任务时会立即提交给线程处理,如果所有线程都在工作就新建线程来处理,直到线程池满了发生错误。
      一般我们在使用它的时候要把maximumPoolSize设得大一些,比如Integer.MAX_VALUE。

    2. LinkedBlockingQueue
      这个queue接收到任务时,如果线程数小于corePollSize,则新建核心线程处理任务,不然就入队等候。这个queue没有最大值限制,而正在工作的最多就corePoolSize个线程,maximumPololSize不会起作用。

    3. ArrayBlockingQueue
      这个queue接收到任务时,如果线程数小于corePollSize,则新建核心线程处理任务,不然就入队等候。如果队列满了则新建非核心线程处理任务,如果线程池跟队列都满了就会报错。

    4. DelayQueue
      这个queue里面的item必须实现Delayed接口,接收到任务后先入队,达到指定延时时间后才会执行任务。

    好了,扯了这么久,大家不必对这些参数感到恐惧。事实上几乎很少需要我们自己配置这些参数的情况。Java为我们提供了四种封装实现,了解这些参数的含义有助于我们理解这四种封装的实现。

    1. CachedThreadPool

      public static ExecutorService newCachedThreadPool() { 
      return  new ThreadPoolExecutor(
        0,
      Integer.MAX_VALUE, 
      60L, 
      TimeUnit.SECONDS, 
      new SynchronousQueue<Runnable>()); }
      
     看到了我们熟悉的SynchronousQueue以及Integer.MAX_VALUE,我们大概就能猜到,得到的线程池有线程数无限制,有空闲线程则复用空闲线程,若无空闲线程则新建线程以及减少频繁创建/销毁线程,减少系统开销等优点。
    
    1. FixedThreadPool

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

      想必你也想到了,得到的线程池并发数可控(nThreads),并且可以添加很多任务。

    2. ScheduledThreadPool

      public static ScheduledExecutorService newScheduledThreadPool(
      int corePoolSize) { 
      return  new ScheduledThreadPoolExecutor(corePoolSize); 
      }
      
       public ScheduledThreadPoolExecutor(int corePoolSize) {  
       super(corePoolSize, 
       Integer.MAX_VALUE,  DEFAULT_KEEPALIVE_MILLIS, 
       MILLISECONDS, 
       new DelayedWorkQueue()); }
      

      这个没什么好解释的,定时任务。

    3. SingleThreadExecutor

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

    这个跟上面的FixedThreadPool有点类似,只不过这个是单线程而已。

    我们自己需要定制一个线程池的话需要做多方面的考量,参数的设置非常影响程序的性能,大家在使用线程池前,一定要理解这些参数的含义!


    关注一下吧

    相关文章

      网友评论

          本文标题:线程池初探

          本文链接:https://www.haomeiwen.com/subject/facevftx.html