线程池

为什么需要使用线程池?

  • 线程的创建和销毁都需要消耗系统资源,线程池可以复用已有的线程
  • 线程也是对象,线程池可以复用线程对象,减低内存消耗
  • 可以控制并发数量,并发数量过多,可能会导致资源消耗过多
  • 可以对线程进行统一管理

线程池原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SmBclC3n-1595301423983)(https://raw.githubusercontent.com/chenxiao19920206/RedSpiderArticlePhotos/master/java-base/multi-thread/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E4%B8%BB%E8%A6%81%E7%9A%84%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B.png)]

  • 线程池线程数量 < 核心线程数,每次都会创建新的线程去处理任务
  • 线程数量 == 核心线程数,则每次新加的任务都会添加到任务队列,线程会去任务队列取任务执行(体现线程复用)
  • 当任务队列满了 && 线程数 < 最大线程数,则会创建救急线程去执行,执行完会被销毁
  • 如果任务队列满了 && 线程数 = 最大线程数,则会执行拒绝策略

拒绝策略:

  • 默认是抛出异常,ThreadPoolExecutor.AbortPolicy:默认拒绝处理策略,丢弃任务并抛出RejectedExecutionException异常。
  • ThreadPoolExecutor.DiscardPolicy:丢弃新来的任务,但是不抛出异常。
  • ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列头部(最旧的)的任务,然后重新尝试执行程序(如果再次失败,重复此过程)。
  • ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。

shutdown和shutdownNow

  • shutdown方法后处于SHUTDOWN,线程池不接受新的任务,等待任务队列中的任务执行完成
  • shutdownNow方法后处于STOP,线程池不接受新任务,并且在执行中的任务也不执行完强制退出

线程池分类

newCachedThreadPool

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
  • 每个线程都是救急线程,使用完会60s空闲后会被回收掉,采用synchronousQueue,每进来一个任务就会有一个线程去取,任务队列不保存任务
  • 当需要执行很多短时间的任务时,CacheThreadPool的线程复用率比较高, 会显著的提高性能。而且线程60s后会回收,意味着即使没有任务进来,CacheThreadPool并不会占用很多资源。

newFixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}
  • 核心线程数 = 最大线程数,只能创建核心线程,不能创建非核心线程。因为LinkedBlockingQueue的默认大小是Integer.MAX_VALUE,故如果核心线程空闲,则交给核心线程处理;如果核心线程不空闲,则入列等待,直到核心线程空闲
  • 由于线程不会被回收,会一直卡在阻塞,所以没有任务的情况下, FixedThreadPool占用资源更多。
    都几乎不会触发拒绝策略,但是原理不同。FixedThreadPool是因为阻塞队列可以很大(最大为Integer最大值),故几乎不会触发拒绝策略;CachedThreadPool是因为线程池很大(最大为Integer最大值),几乎不会导致线程数量大于最大线程数,故几乎不会触发拒绝策略。

newSingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
  • 单线程线程池,如果这个唯一的线程不空闲,那么新来的任务会存储在任务队列里等待执行。
    newScheduledThreadPool
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue());
}
  • 定时任务线程池
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐