zoukankan      html  css  js  c++  java
  • 四种线程池的解析

    Tips:关注微信公众号:松花皮蛋的黑板报,获取更多精彩!

    首先我们先看一下获取四种线程池的代码:

        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);
        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
    
    

    可以发现这四种线程池都是由Executors类生成的。依次点开四个方法的内部实现发现,它们最终调用的都是同一个ThreadPoolExecutor()的构造器,而区别在于构造器的参数不同。我们来看下ThreadPoolExecutor的参数列表:

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

    正是由于这几个参数的不同导致了四种线程池的工作机制不同。参考源码对于参数的注释,我们列出参数的含义。

    • corePoolSize:核心线程数量,常驻在线程池中的线程,即使它们是空闲的,也不会销毁,除非设置allowCoreThreadTimeOut的值。
    • maximumPoolSize:线程池最大线程数量
    • keepAliveTime:超过核心数量的额外线程也就是非核心线程,在空闲指定的最大时间后被销毁。(假设时间为5s,核心线程数为2,当前线程为4,则超过核心线程数的其余两个线程在空闲5秒后会被销毁。)
    • unit:时间单位
    • workQueue:等待队列
    • threadFactory:生成线程的工厂
    • handler:当等待队列容量满以及线程池数量达到最大时,如何处理新的任务。
      • AbortPolicy(默认):直接抛出异常
      • CallerRunsPolicy:交给调用者所在线程执行。(假设当前调用者线程是Main,那么就交给Main处理)
      • DiscardOldestPolicy:丢弃最久未处理的任务,再执行当前任务。(最久未处理的,在队列中其实就是队列头节点,查看源码的确调用是poll()方法)
      • DiscardPolicy:丢掉该任务,并且不抛异常。

    线程池的工作机制:

    当持续往线程池添加任务,
    当前线程数量小于核心线程数量的时候,新增线程。
    当前线程数量达到核心线程数量的时候,将任务放入等待队列。
    当等待队列满的时候,继续创建新线程。
    当线程池数量达到最大并且等待队列也满的时候,采取拒绝服务策略。

    接下来我们就根据参数来分析不同的线程池:

    FixedThreadPool

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

    我们可以看到corePoolSize核心线程数量和maximumPoolSize最大线程数量是一致的,并且keepAliveTime为0。workQueue是LinkedBlockingQueue,这是一个链表阻塞队列。可以得出结论:该线程池是一个固定数量的线程池,并且有一个无界的等待队列。我们可以推导出该线程池适合处理任务量平稳的场景。例如平均一秒接收10个任务,接收任务量曲线不会很陡峭。

    适合场景:适合少量的大任务(大任务处理慢,如果线程数量多的话,反而在切换线程上下文时损耗,所以控制线程在一定的数量)。

    CachedThreadPool

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

    我们可以看到corePoolSize核心线程池为0,代表该线程没有核心线程池,意味着线程都是可被回收销毁的,线程池中有时会是空的。并且maximumPoolSize是int最大值,相当于代表该线程池可以无限创建线程。keepAliveTime为60,代表空闲60秒回收线程。workQueue是SynchronousQueue,该同步队列是一个没有容量队列,即一个任务到来后,要等待线程来消费,才能再继续添加任务。我们推导出该线程池适合处理平时没什么任务量,但有时任务量瞬间剧增的场景。

    适合场景:大量的小任务(每个任务处理快,不会频繁出现线程处理一半时,切换其他线程)。

    ScheduledThreadPool

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

    我们可以看到该线程池参数最大的区别在于workQueue是DelayedWorkQueue。该队列是一个按延迟时间从小到大排序的堆。并且当队列头节点的延迟时间小于0的时候返回该节点。所以该线程池可以指定一个时间进行定时任务。也可以通过添加任务时递增延迟时间,来进行周期任务。

    适合场景:定时任务或者周期任务。

    SingleThreadExecutor

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

    我们可以看到该线程池的corePoolSize核心线程数量和maximumPoolSize最大线程数量都是1,代表该线程有且只有一个固定的线程,既然是单线程,所以该线程池实现的是串行操作,没有并发效果。workQueue是LinkedBlockingQueue,这是一个链表阻塞队列。所以该线程池适合执行串行执行队列中的任务。

    适合场景:按顺序串行处理的任务。

    可能读者会好奇keepAliveTime为0代表的含义? 是立即回收线程还是永不回收呢?

    keepAliveTime参数注释明确指明只对非核心线程有用。
    我们可以从ScheduledThreadPool的源码中推测,如果0代表是永不回收的话,那么ScheduledThreadPool一旦创建出非核心线程的话就不会回收了?这样是很不合理的。所以笔者认为0代表立即回收。

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


    BLOG地址:www.liangsonghua.me

    关注微信公众号:松花皮蛋的黑板报,获取更多精彩!

    公众号介绍:分享在京东工作的技术感悟,还有JAVA技术和业内最佳实践,大部分都是务实的、能看懂的、可复现的

     
  • 相关阅读:
    flutter 右滑返回上一页
    flutter 的Animation简单了解
    Flutter Offstage、Visibility隐藏/可见
    flutter手势
    Flutter生命周期
    flutter 路由动画
    flutter 保持页面状态
    flutter 不规则底部工具栏实现
    flutter 主页面底部导航栏实现以及主题风格设置
    flutter DropdownButton使用
  • 原文地址:https://www.cnblogs.com/liangsonghua/p/www_liangsonghua_me_35.html
Copyright © 2011-2022 走看看