文章目录

  • 线程池
    • 11.1 线程池概述
    • 11.2 线程池架构
    • 11.3 线程池使用方式
    • 11.4 线程池底层原则
    • 11.5 线程池的七个参数
    • 11.6 线程池底层工作流程
    • 11.7 自定义线程池

线程池

11.1 线程池概述

连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用

线程池(英语:thread pool)一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度

线程池的优势: 线程池做的工作只要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超过数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。

线程池的特点:

  1. 降低资源消耗: 通过重复利用已创建的线程降低线程创建和销毁造成的销耗。
  2. 提高响应速度: 当任务到达时,任务可以不需要等待线程创建就能立即执行。
  3. 提高线程的可管理性: 线程是稀缺资源,如果无限制的创建,不仅会销耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

11.2 线程池架构

Java 中的线程池是通过 Executor 框架实现的,该框架中用到了 Executor,Executors,ExecutorService,ThreadPoolExecutor 这几个类

说明:Executors为工具类,I为接口类,C为实现类

11.3 线程池使用方式

Executors.newFixedThreadPool(int):一池N线程

ExecutorService threadPool1 = Executors.newFixedThreadPool(5); //5个窗口

Executors.newSingleThreadExecutor():一池一线程

ExecutorService threadPool2 = Executors.newSingleThreadExecutor(); //一个窗口

Executors.newCachedThreadPool():一池可扩容根据需求创建线程

 ExecutorService threadPool3 = Executors.newCachedThreadPool();

执行线程execute()
关闭线程shutdown()

void execute(Runnable command);参数为Runnable接口类,可以通过设置lambda

具体案例代码案例

public class ThreadPoolTest {public static void main(String[] args) {ExecutorService threadPool1 = Executors.newFixedThreadPool(5);ExecutorService threadPool2 = Executors.newSingleThreadExecutor();ExecutorService threadPool3 = Executors.newCachedThreadPool();// 是个顾客请求try{for (int i = 1; i <= 10; i++) {// 到此时执行execute()方法才创建线程threadPool2.execute(()->{System.out.println(Thread.currentThread().getName()+" 办理业务");});}}finally {// 关闭线程threadPool1.shutdown();}}
}

一池N线程输出结果为:

pool-1-thread-2 办理业务
pool-1-thread-1 办理业务
pool-1-thread-3 办理业务
pool-1-thread-5 办理业务
pool-1-thread-4 办理业务
pool-1-thread-2 办理业务
pool-1-thread-1 办理业务
pool-1-thread-4 办理业务
pool-1-thread-5 办理业务
pool-1-thread-3 办理业务

一池一线程输出结果为:

pool-2-thread-1 办理业务
pool-2-thread-1 办理业务
pool-2-thread-1 办理业务
pool-2-thread-1 办理业务
pool-2-thread-1 办理业务
pool-2-thread-1 办理业务
pool-2-thread-1 办理业务
pool-2-thread-1 办理业务
pool-2-thread-1 办理业务
pool-2-thread-1 办理业务

一池可扩容根据需求创建线程输出结果为:

pool-3-thread-1 办理业务
pool-3-thread-5 办理业务
pool-3-thread-4 办理业务
pool-3-thread-7 办理业务
pool-3-thread-2 办理业务
pool-3-thread-3 办理业务
pool-3-thread-8 办理业务
pool-3-thread-6 办理业务
pool-3-thread-9 办理业务
pool-3-thread-10 办理业务

11.4 线程池底层原则

通过查看上面三种方式创建对象的类源代码
都有 new ThreadPoolExecutor 具体查看该类的源代码,涉及七个参数

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.acc = System.getSecurityManager() == null ?null :AccessController.getContext();// 常驻线程数量(核心)this.corePoolSize = corePoolSize;// 最大线程数量this.maximumPoolSize = maximumPoolSize;// 阻塞队列(排队的线程放入)this.workQueue = workQueue;// 线程存活时间this.keepAliveTime = unit.toNanos(keepAliveTime);// 线程工厂,用于创建线程 线程工厂,用于创建线程this.threadFactory = threadFactory;// 拒绝测试(线程满了this.handler = handler;
}

11.5 线程池的七个参数

具体代码中的七个参数讲解:

int corePoolSize,常驻线程数量(核心)

int maximumPoolSize,最大线程数量

long keepAliveTime,TimeUnit unit,线程存活时间

BlockingQueue workQueue,阻塞队列(排队的线程放入)

ThreadFactory threadFactory,线程工厂,用于创建线程

RejectedExecutionHandler handler,拒绝测试(线程满了)

具体工作流程是:

  1. 在执行创建对象的时候不会创建线程,创建线程的时候execute()才会创建
  2. 先到常驻线程,满了之后再到阻塞队列进行等待,阻塞队列满了之后,在往外扩容线程,扩容线程不能大于最大线程数。大于最大线程数和阻塞队列之和后,会执行拒绝策略。
  3. 阻塞队列为3,常驻线程数2,最大线程数5

11.6 线程池底层工作流程

对流程图的解释

现在假设来了9个线程,在执行execute()方法才创建线程。

第1-2个线程进入线程池创建

第3-5个线程进入阻塞队列

第6-8个线程会为他们创建新线程执行(直接运行线程6而非线程3)

第9个线程会被拒绝

总结来说:先到常驻线程,满了之后再到阻塞队列进行等待,阻塞队列满了之后,在往外扩容线程,扩容线程不能大于最大线程数。大于最大线程数和阻塞队列之和后,会执行拒绝策略。

具体的拒绝策略有:

  1. 抛异常-AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常运行
  2. 谁调用找谁-CallerRunsPolicy:“调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量
  3. 抛弃最久执行当前-DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中,尝试再次提交当前任务
  4. 不理不问-Policydiscard:该策略默默地丢弃无法处理的任务,不予任何处理也不抱出异常。如果允许任务丢失,这是最好的一种策略

11.7 自定义线程池

实际在开发中不允许使用Executors创建,而是通过ThreadPoolExecutor的方式,规避资源耗尽风险

说明:Executors 返回的线程池对象的弊端如下:

1)FixedThreadPool 和 SingleThreadPool:

允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

2)CachedThreadPool 和 ScheduledThreadPool:

允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

ExecutorService threadPool = new ThreadPoolExecutor(// 常驻核心线程2,// 最大线程数量5,// 线程存活时间2L,TimeUnit.SECONDS,// 阻塞队列new ArrayBlockingQueue<>(3),// 线程工厂Executors.defaultThreadFactory(),// 拒绝策略new ThreadPoolExecutor.AbortPolicy()
);

其他都同理,只是调用ThreadPoolExecutor类,自定义参数

public class ThreadPoolTest {public static void main(String[] args) {// 组定义线程池ExecutorService threadPool = new ThreadPoolExecutor(// 常驻线程数量(核心)2个2,// 最大线程数量5个5,// 线程存活时间:2秒2L,TimeUnit.SECONDS,// 阻塞队列new ArrayBlockingQueue<>(3),// 默认线程工厂Executors.defaultThreadFactory(),// 拒绝策略。抛出异常new ThreadPoolExecutor.AbortPolicy());try{for (int i = 1; i <= 8; i++) {threadPool.execute(()->{System.out.println(Thread.currentThread().getName()+" 办理业务");});}}catch (Exception e){e.printStackTrace();}finally {// 关闭线程池threadPool.shutdown();}}
}

输出结果

pool-1-thread-1 办理业务
pool-1-thread-4 办理业务
pool-1-thread-3 办理业务
pool-1-thread-2 办理业务
pool-1-thread-3 办理业务
pool-1-thread-4 办理业务
pool-1-thread-1 办理业务
pool-1-thread-5 办理业务

如果线程数大于 最大线程数量+阻塞队列容量最大线程数量+阻塞队列容量最大线程数量+阻塞队列容量 则抛出异常

【JUC并发编程11】线程池相关推荐

  1. [转]Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  2. Java并发编程:线程池的使用

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...

  3. 【Java 并发编程】线程池机制 ( ThreadPoolExecutor 线程池构造参数分析 | 核心线程数 | 最大线程数 | 非核心线程存活时间 | 任务阻塞队列 )

    文章目录 前言 一.ThreadPoolExecutor 构造参数 二.newCachedThreadPool 参数分析 三.newFixedThreadPool 参数分析 四.newSingleTh ...

  4. 【Java 并发编程】线程池机制 ( 线程池示例 | newCachedThreadPool | newFixedThreadPool | newSingleThreadExecutor )

    文章目录 前言 一.线程池示例 二.newCachedThreadPool 线程池示例 三.newFixedThreadPool 线程池示例 三.newSingleThreadExecutor 线程池 ...

  5. 《转载》Python并发编程之线程池/进程池--concurrent.futures模块

    本文转载自 Python并发编程之线程池/进程池--concurrent.futures模块 一.关于concurrent.futures模块 Python标准库为我们提供了threading和mul ...

  6. (转)Java并发编程:线程池的使用

    背景:线程池在面试时候经常遇到,反复出现的问题就是理解不深入,不能做到游刃有余.所以这篇博客是要深入总结线程池的使用. ThreadPoolExecutor的继承关系 线程池的原理 1.线程池状态(4 ...

  7. Java并发编程一线程池简介

    推荐:Java并发编程汇总 Java并发编程一线程池简介 为什么我们需要使用线程池? 我们知道线程是一种比较昂贵的资源,我们通过程序每创建一个线程去执行,其实操作系统都会对应地创建一个线程去执行我们的 ...

  8. Java并发编程一线程池的五种状态

    推荐:Java并发编程汇总 Java并发编程一线程池的五种状态 原文地址 Java多线程线程池(4)–线程池的五种状态 正文 线程池的5种状态:Running.ShutDown.Stop.Tidyin ...

  9. Java并发编程:线程池

    一.为什么使用线程池 使用线程的时候直接就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降 ...

  10. Java并发编程之线程池及示例

    1.Executor 线程池顶级接口.定义方法,void execute(Runnable).方法是用于处理任务的一个服务方法.调用者提供Runnable 接口的实现,线程池通过线程执行这个 Runn ...

最新文章

  1. 使用nc传输文件和目录【转】
  2. PHP7Grafika,PHP图片处理库Grafika详细教程(3):图像属性处理
  3. 数据库系统概论:第八章 数据库编程
  4. java 配置文件加载_Java加载配置文件类
  5. hp laser103 属性没有配置项_(常见解决方法)UEditor报错“后端配置项没有正常加载,上传插件不能正常使用”...
  6. linux对目录进行操作,Linux 基础:对文件和目录进行操作的 Linux 和 Unix 命令 笔记...
  7. 共筑计算新生态 共赢数字新时代
  8. html xsl xml文件,用XSL显示XML文件看起来像HTML
  9. Fragment生命周期(转)
  10. 思科CDP/LLDP协议
  11. 计算机怎样更新卡驱动,显卡驱动怎么升级
  12. 股票大作手回忆录投机感悟
  13. php 写入exif,用PHP将EXIF写入JPG
  14. 手机点餐系统概述_廖师兄 微信点餐系统 springcloud学习笔记
  15. 2021鹏业安装算量软件常见问题整理(二十)
  16. 亚马逊正在逐渐压垮出版社,帮了世界一把
  17. GP232RL直接替代FT232RL串口芯片uart接口
  18. Jmeter源码分析(二)
  19. 关于QT中“崩溃”问题
  20. 当仁不让!一文看尽MWC舞台上的人工智能

热门文章

  1. 【linux】关于分析系统问题的前几分钟
  2. FCKeditor 2.6.4.1 初始化值不能显示中文问题
  3. 配置错误定义了重复的“system.web.extensions/scripting/scriptResourceHandler” 解决办法...
  4. xp与Vista双系统 相关问题
  5. Updater Application Block for .NET
  6. VC中退出应用程序-几种很有用的方法
  7. linux驱动篇之 driver_register 过程分析(二)bus_add_driver
  8. 《研磨设计模式》chap11 代理proxy模式
  9. [PSA]-PSA Certified简介
  10. 如何设计复用性较好的类?