首页 > 编程语言 > 详细

线程池ThreadPoolExecutor

时间:2020-05-06 01:55:36      阅读:107      评论:0      收藏:0      [点我收藏+]

 

正文

为什么要用线程池?

  几乎所有需要异步或者并发执行任务的程序都可以使用线程池。合理使用会给我们带来以下好处。
  • 降低系统消耗:重复利用已经创建的线程降低线程创建和销毁造成的资源消耗。
  • 提高响应速度: 当任务到达时,任务不需要等到线程创建就可以立即执行。
  • 提供线程可以管理性: 可以通过设置合理分配、调优、监控。

 

线程池工作流程
  1、判断核心线程池里的线程是否都有在执行任务,否——》创建一个新工作线程来执行任务。是——》走下个流程。
  2、判断工作队列是否已满,否——》新任务存储在这个工作队列里,是——》走下个流程。
  3、判断线程池里的线程是否都在工作状态,否——》创建一个新的工作线程来执行任务,是——》走下个流程。
  4、按照设置的策略来处理无法执行的任务。


线程池构造函数

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

  1. corePoolSize:
    核心线程池大小,当提交一个任务时,线程池会创建一个线程来执行任务,即使其他空闲的核心线程能够执行新任务也会创建,等待需要执行的任务数大于线程核心大小就不会继续创建。
    如果是刚开始创建的线程池,还没有任务,那么来一个任务我就创建一个核心线程,目的就是为了进行创建足够我用的核心线程。到后面我的核心线程是一直保持的。


  2. maximumPoolSize:
    线程池最大数,允许创建的最大线程数,如果队列满了,并且已经创建的线程数小于最大线程数,则会创建新的线程执行任务。如果是无界队列,那就永远不会满,这个参数就没用了。
    当队列满了的时候,就会认为核心线程还不能满足当前任务的需求,那就会扩展多几个辅助线程来帮忙干活,当辅助线程空闲了,没事干了,就把这些辅助线程取消,只保留核心线程。


  3. keepAliveTime:
    线程保持活动时间,线程池工作线程空闲后,保持存活的时间,所以如果任务很多,并且每个任务执行时间较短,可以调大时间,提高线程利用率。


  4. unit:
    线程保持活动时间单位,天(DAYS)、小时(HOURS)、分钟(MINUTES、毫秒MILLISECONDS)、微秒(MICROSECONDS)、纳秒(NANOSECONDS)

 

  5. workQueue: 任务队列,保存等待执行的任务的阻塞队列。
    一般来说可以选择如下阻塞队列:
      ArrayBlockingQueue: 基于数组的有界阻塞队列。创建时要指定大小
      LinkedBlockingQueue: 基于链表的阻塞队列。创建时可以指定大小,不指定时就是int的最大值
      SynchronizedQueue: 一个不存储元素的阻塞队列。
      PriorityBlockingQueue: 一个具有优先级的阻塞队列。


  6. threadFactory:设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。


  7. handler: 饱和策略也叫拒绝策略。当队列和线程池都满了,即达到饱和状态。所以需要采取策略来处理新的任务。默认策略是AbortPolicy。
      AbortPolicy: 直接抛出异常。
      CallerRunsPolicy: 队列满,不丢任务,不抛异常,若添加到线程池失败,那么主线程会自己去执行该任务。
      DiscardOldestPolicy: 删除队列中最旧的任务(最早进入队列的任务),尝试重新提交新的任务。
      DiscardPolicy: 不处理,直接丢掉。
      当然可以根据自己的应用场景,实现RejectedExecutionHandler接口自定义策略。

 

  注意:线程池里面并没有什么核心线程、什么辅助线程,我在上面的说法,只是为了让你们更好区分那几个参数的作用。线程池中所有线程都是同等地位的。当你池中线程数量大于核心线程数量时,就会把空闲的线程取消,以达到将池中线程数量降到核心数量大小的目的。线程池的线程数量最终是维持在核心线程数量。


向线程池提交任务
  可以使用execute()和submit() 两种方式提交任务。
    execute():无返回值,所以无法判断任务是否被执行成功。
    submit():用于提交需要有返回值的任务。线程池返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()来获取返回值,get()方法会阻塞当前线程知道任务完成。get(long timeout,TimeUnit unit)可以设置超市时间。


关闭线程池
  可以通过shutdown()或shutdownNow()来关闭线程池。它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt来中断线程,所以无法响应终端的任务可以能永远无法停止。
    shutdownNow首先将线程池状态设置成STOP,然后尝试停止所有的正在执行或者暂停的线程,并返回等待执行任务的列表。所有线程我都一视同仁,全部干掉,不管你是不是在干活。
    shutdown只是将线程池的状态设置成shutdown状态,然后中断所有没有正在执行任务的线程。空闲的线程会被干掉,工作的线程会等他做完。
  只要调用两者之一,isShutdown就会返回true,当所有任务都已关闭,isTerminaed就会返回true。

  一般来说调用shutdown方法来关闭线程池,如果任务不一定要执行完,可以直接调用shutdownNow方法。


Executors

  任务池ThreadPoolExecutor可以通过Executors来快速创建,Executors主要可以生成5种任务池(还有两种没用过)

    SingleThreadExecutor:只有一个线程的线程池(固定只有一个线程,任务队列默认长为Integer.MAX_VALUE,线程不会被空闲取消)

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


    FixedThreadPool:固定线程数量的线程池(线程数量固定,任务队列任务队列默认长为Integer.MAX_VALUE,线程不会被空闲取消)

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

 


    CachedThreadPool:可缓存的线程池(不会缓存任务列表,来一个任务,就开一个线程,线程空闲60秒就取消,线程数量最大为Integer.MAX_VALUE)

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

 



    ScheduledThreadPool:固定数量可执行周期性任务的线程池(最大线程数量为Integer.MAX_VALUE,任务队列DelayedWorkQueue初始16,可自增,每次增加50%,最大为Integer.MAX_VALUE)

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }


    SingleThreadScheduledExecutor:只有一个线程的可执行周期性任务的线程池(核心线程就一个,最大线程数量为Integer.MAX_VALUE,任务队列DelayedWorkQueue初始16,可自增,每次增加50%,最大为Integer.MAX_VALUE)

    public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }    

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }


  注意:

    SingleThreadExecutor、FixedThreadPool、CachedThreadPool并不是一个新的类,他们都是ThreadPoolExecutor的对象,只是参数不同
    ScheduledThreadPoolExecutor这个才是一个新的类,继承自ThreadPoolExecutor


  很多人对不赞成使用Executors来创建线程池,看过前面内容应该就知道了,Executors创建的线程池,要么任务队列为Integer.MAX_VALUE,要么最大线程为Integer.MAX_VALUE,很明显在实际系统中这是不可能的。大部分情况下,任务太多了,可能会导致内存溢出,所以我们都是选择丢弃的。另外我们服务器的资源有限,不可能把所有CPU都给线程池使用,所以我们就必须按实际控制核心线程数量和最大线程数量了

  因此,使用线程池,最好还是自己用ThreadPoolExecutor的构造函数来创建,反正那几个参数也不复杂。

 

线程池ThreadPoolExecutor

原文:https://www.cnblogs.com/chongcheng/p/12833569.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!