线程池的使用

Java知识点总结:想看的可以从这里进入

9.2.3、使用线程池

线程池是一种线程使用模式,为了应对频繁的创建和销毁线程会带来调度开销,影响缓存局部性和整体性能而创建。线程池内可以维护多个线程,不需要频繁和销毁,而是统一进行管理,只需要管理者分配可并发执行的任务即可,避免了在处理短时间任务时创建与销毁线程的代价。

线程池的原理和数据库连接池是类似的,为了能够复用线程、限制并发数以及减少上下文切换带来的资源消耗,使用池化的技术对线程进行管理。

线程池是事先创建若干个可执行的线程放入一个池中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。线程池做的工作主要是控制运行的线程的数量,处理过程中将任务加入队列,然后在线程创建后启动这些任务,如果线程数超过了最大数量,超出的数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。

jdk给我们提供了Executor框架、Executors工具类,来创建和使用线程池,架构图如下:

在这里插入图片描述

​ 1. 通过 ThreadPoolExecutor 创建的线程池

​ 2. 通过 Executors 创建的线程池

1、Executor框架
  • Executor接口:只有一个待实现的方法,用来执行一项Runnable任务

    在这里插入图片描述

  • ExecutorService接口:对Executor进行了扩展,提供了更多的方法,一般使用它来代替Executor

    在这里插入图片描述

  • ThreadPoolExecutor:Executor框架的正真实现者,实现了execute方法

    private final HashSet<Worker> workers = new HashSet<>();		//Worker集合,线程池
    private final BlockingQueue<Runnable> workQueue;	//阻塞队列,要执行的任务
    final void runWorker(Worker w);		//Worker调用这个方法,可以从阻塞队列中获取任务来执行。
    

    其构造函数接收的参数:

    1public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
    							BlockingQueue<Runnable> workQueue) {}
    2public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory) {}
    3public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler) {}
    4public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,
                                  RejectedExecutionHandler handler) {}
    
    1. corePoolSize:核心线程数,也即最小的线程数,线程池会维护一个最小的线程数。

    2. maximumPoolSize:最大的线程数

    3. keepAliveTime:线程的存活时间。默认情况下,只有当线程池中的线程数大于corePoolSize时,才会起作用,直到线程池中的线程数不大于corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;

    4. unit:存活时间的单位

      TimeUnit.DAYS; //天
      TimeUnit.HOURS; //小时
      TimeUnit.MINUTES; //分钟
      TimeUnit.SECONDS; //秒
      TimeUnit.MILLISECONDS; //毫秒
      TimeUnit.MICROSECONDS; //微妙
      TimeUnit.NANOSECONDS; //纳秒

    5. workQueue:存放任务的阻塞队列,使用阻塞队列可以管理线程的组合和唤醒。

      ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
      LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于数组的
      SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 LinkedBlockingQuene;
      priorityBlockingQuene:具有优先级的无界阻塞队列;

    6. threadFactory:创建线程的线程工厂

      Executors.defaultThreadFactory():工具类的默认工厂

    7. handler:线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:

      • new ThreadPoolExecutor.AbortPolicy(默认,中止策略):丢弃任务,抛出

        RejectedExecutionException异常阻止系统正常运行。

      • new ThreadPoolExecutor.CallerRunsPolicy(回退策略):将提交的任务会退给调用线程,让调用线程执行。

      • new ThreadPoolExecutor.DiscardOldestPolicy(抛弃最老策略):丢弃队列最前面的任务,并将新任务加入

      • new ThreadPoolExecutor.DiscardPolicy(抛弃策略):丢弃任务,但是不抛出异常。

    线程池监控API:

    • getActiveCount() :获取线程池中正在执行任务的线程数量
    • getCompletedTaskCount():获取已执行完毕的任务数
    • getTaskCount() :获取线程池已执行与未执行的任务总数
    • getPoolSize():获取线程池当前的线程数
    • getQueue().size():获取队列中的任务数
  • 如果线程池中的线程数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
  • 如果线程池中的线程数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列。
  • 如果线程池中的线程数量大于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
  • 如果线程池中的线程数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过handlerf所指定的策略来处理此任务。
  • 当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数
2、Executors工具类

Executors它是一个线程工具类,内部封装的方法能帮助我们创建线程池:

在这里插入图片描述

**注意:**阿里的开发手册中是禁止使用Executors的

虽然Exectors工具类给我们提供了一些方法,可以方便的获取一些配置好的ThreadPoolExecutor类,但是它们的配置有一些不合理的地方,在实际生产中,我们一般会自定义线程池,而不是使用jdk提供的。以下是阿里中的规定:

【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors返回的线程池对象的弊端如下:
1)FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAx_vALUE,可能会堆积大量的请求,从而导致ooM。
2)CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为snteger.MAx_vALUE,可能会创建大量的线程,从而导致ooM。
3、创建线程池
Executors

在这里插入图片描述

  • 创建固定容量的线程池

    在这里插入图片描述

    在这里插入图片描述

  • 创建单个线程的线程池

    在这里插入图片描述

    在这里插入图片描述

  • 容量可变的线程池
    在这里插入图片描述
    在这里插入图片描述

ThreadPoolExecutor

Executors工具类创建的线程池,本质上也是构建ThreadPoolExecutor类,所以我们也可以直接根据ThreadPoolExecutor类的四个构造方法去创建线程。

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) {}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辰 羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值