> 线程池(一)What Is ThreadPool - Yuyy
Yuyy
Yuyy
线程池(一)What Is ThreadPool

一、写在前面

1.1 线程池是什么

线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL。

线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。

使用线程池可以带来一系列好处:

  • 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
  • 提高响应速度:任务到达时,无需等待线程创建即可立即执行。
  • 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。
  • 提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。

1.2 线程池解决的问题是什么

线程池解决的核心问题就是资源管理问题。在并发环境下,系统不能够确定在任意时刻中,有多少任务需要执行,有多少资源需要投入。这种不确定性将带来以下若干问题:

  1. 频繁申请/销毁资源和调度资源,将带来额外的消耗,可能会非常巨大。
  2. 对资源无限申请缺少抑制手段,易引发系统资源耗尽的风险。
  3. 系统无法合理管理内部的资源分布,会降低系统的稳定性。

为解决资源分配这个问题,线程池采用了“池化”(Pooling)思想。池化,顾名思义,是为了最大化收益并最小化风险,而将资源统一在一起管理的一种思想。

Pooling is the grouping together of resources (assets, equipment, personnel, effort, etc.) for the purposes of maximizing advantage or minimizing risk to the users. The term is used in finance, computing and equipment management.——wikipedia

“池化”思想不仅仅能应用在计算机领域,在金融、设备、人员管理、工作管理等领域也有相关的应用。

在计算机领域中的表现为:统一管理IT资源,包括服务器、存储、和网络资源等等。通过共享资源,使用户在低投入中获益。除去线程池,还有其他比较典型的几种使用策略包括:

  1. 内存池(Memory Pooling):预先申请内存,提升申请内存速度,减少内存碎片。
  2. 连接池(Connection Pooling):预先申请数据库连接,提升申请连接的速度,降低系统的开销。
  3. 实例池(Object Pooling):循环使用对象,减少资源在初始化和释放时的昂贵损耗。

二、线程池剖析

2.1 相关类

http://bed.yuyy.info/image-20210307100925446.png

  1. Executor

    线程池的顶级接口定义了线程池的最基本方法,提供了一种思想:将任务提交和任务执行进行解耦。用户无需关注如何创建线程,如何调度线程来执行任务,用户只需提供Runnable对象,将任务的运行逻辑提交到执行器(Executor)中,由Executor框架完成线程的调配和任务的执行部分。

http://bed.yuyy.info/image-20210309221032836.png

  1. ExecutorService

    定义线程管理,调度常用方法。增加了一些能力:(1)扩充执行任务的能力,补充可以为一个或一批异步任务生成Future的方法;(2)提供了管控线程池的方法,比如停止线程池的运行。

http://bed.yuyy.info/image-20210309221140179.png

  1. AbstractExecutorService

    上层的抽象类,将执行任务的流程串联了起来,保证下层的实现只需关注一个执行任务的方法即可。

       public Future<?> submit(Runnable task) {//ExecutorService接口的方法
           if (task == null) throw new NullPointerException();
           RunnableFuture<Void> ftask = newTaskFor(task, null);
           execute(ftask);//Executor接口的方法
           return ftask;
       }
    
  2. ThreadPoolExecutor

    是线程池的核心实现类,用来执行被提交任务。一方面维护自身的生命周期,另一方面同时管理线程和任务

  3. ScheduledThreadPoolExecutor

    另一个关键实现类,可以进行延迟或者定期执行任务。ScheduledThreadPoolExecutor比Timer定时器更灵活,功能更强大

  4. **Future接口与FutureTask实现类 **

    代表异步计算的结果

  5. Runnable接口和Callable接口的实现类

    都是可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor执行的任务

2.2 缓冲队列

  1. SynchronousQueue

没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素。
使用SynchronousQueue阻塞队列一般要求maximumPoolSizes为无界,避免线程拒绝执行操作。

  1. LinkedBlockingQueue

无界缓存等待队列,链表实现。初始容量为int最大值,超过核心线程数的任务全都存在此队列里。在使用此阻塞队列时maximumPoolSizes就相当于无效了

  1. ArrayBlockingQueue

有界缓存等待队列,数组实现,可以指定缓存队列的大小。

三个队列都实现了BlockingQueue阻塞队列接口,队列里无数据时消费者阻塞,队列无法存数据时生产者阻塞

http://bed.yuyy.info/image-20210309211730563.png

2.3 饱和策略

http://bed.yuyy.info/image-20210309214101007.png

  1. AbortPolicy

    拒绝任务,并抛异常,起到提醒的作用

  2. CallerRunsPolicy

    有存放任务的线程来执行

  3. DiscardPolicy

    抛弃任务,不抛异常。用于无关紧要的任务

  4. DiscardOldestPolicy

    抛弃队列里最早的任务,不抛异常

三、线程池工作流程

线程池在内部实际上构建了一个生产者消费者模型,将线程和任务两者解耦,并不直接关联,从而良好的缓冲任务,复用线程。线程池的运行主要分成两部分:任务管理、线程管理。任务管理部分充当生产者的角色,当任务提交后,线程池会判断该任务后续的流转:

  1. 直接申请线程执行该任务

  2. 缓冲到队列中等待线程执行

  3. 拒绝该任务

线程管理部分是消费者,它们被统一维护在线程池内,根据任务请求进行线程的分配,当线程执行完任务后则会继续获取新的任务去执行,最终当线程获取不到任务的时候,线程就会被回收。

http://bed.yuyy.info/image-20210309223829054.png

:(1)tomcat内部线程池处理步骤与此不同,当工作线程大于等于核心线程,但小于最大工作线程时,任务不会进等待队列,而是使用非核心线程来执行

​ (2)创建线程池时,默认是没有线程的,用的时候再创建

https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html

四、线程池的工具类Executors

提供了四种快捷创建线程池的方法:

  1. newCachedThreadPool

    可缓存线程池,可灵活回收空闲线程,有求必应,适合并发不高的场景。

       public static ExecutorService newCachedThreadPool() {
           return new ThreadPoolExecutor(0, Integer.MAX_VALUE, //没有核心线程
                                         60L, TimeUnit.SECONDS, //非核心线程存活一分钟,实现缓存线程的功能
                                         new SynchronousQueue<Runnable>()); //无缓冲等待队列,有求必应
       }
    
  2. newFixedThreadPool
    定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

       public static ExecutorService newFixedThreadPool(int nThreads) { //初始线程数
           return new ThreadPoolExecutor(nThreads, nThreads, //核心线程数等于最大线程数,即没有非核心线程
                                         0L, TimeUnit.MILLISECONDS,
                                         new LinkedBlockingQueue<Runnable>()); //无界队列,可存大量线程且不触发饱和策略
       }
    
  3. newSingleThreadExecutor
    单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

       public static ExecutorService newSingleThreadExecutor() {
           return new FinalizableDelegatedExecutorService
               (new ThreadPoolExecutor(1, 1, //只有一个工作线程
                                       0L, TimeUnit.MILLISECONDS,
                                       new LinkedBlockingQueue<Runnable>())); //多于任务全进无界队列里
       }
    
  4. newScheduledThreadPool

    定长线程池,支持定时及周期性任务执行。

       public ScheduledThreadPoolExecutor(int corePoolSize) { //设置工作线程的数量
           super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
                 new DelayedWorkQueue());//延迟队列
       }
       //执行方法
       public ScheduledFuture<?> schedule(Runnable command,//任务
                                          long delay,//延迟时间
                                          TimeUnit unit) {//时间单位
           if (command == null || unit == null)
               throw new NullPointerException();
           RunnableScheduledFuture<?> t = decorateTask(command,
               new ScheduledFutureTask<Void>(command, null,
                                             triggerTime(delay, unit)));
           delayedExecute(t);
           return t;
       }
    

发表评论

textsms
account_circle
email

Yuyy

线程池(一)What Is ThreadPool
一、写在前面 1.1 线程池是什么 线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL。 线程过多会带来额外的开销,其中包括创建销毁线程的开…
扫描二维码继续阅读
2021-03-09
友情链接
标签
文章归档
近期文章