【JUC并发编程11】线程池
文章目录
- 线程池
- 11.1 线程池概述
- 11.2 线程池架构
- 11.3 线程池使用方式
- 11.4 线程池底层原则
- 11.5 线程池的七个参数
- 11.6 线程池底层工作流程
- 11.7 自定义线程池
线程池
11.1 线程池概述
连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用
线程池(英语:thread pool)一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度
线程池的优势: 线程池做的工作只要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超过数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。
线程池的特点:
- 降低资源消耗: 通过重复利用已创建的线程降低线程创建和销毁造成的销耗。
- 提高响应速度: 当任务到达时,任务可以不需要等待线程创建就能立即执行。
- 提高线程的可管理性: 线程是稀缺资源,如果无限制的创建,不仅会销耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
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,拒绝测试(线程满了)
具体工作流程是:
- 在执行创建对象的时候不会创建线程,创建线程的时候execute()才会创建
- 先到常驻线程,满了之后再到阻塞队列进行等待,阻塞队列满了之后,在往外扩容线程,扩容线程不能大于最大线程数。大于最大线程数和阻塞队列之和后,会执行拒绝策略。
- 阻塞队列为3,常驻线程数2,最大线程数5
11.6 线程池底层工作流程
对流程图的解释
现在假设来了9个线程,在执行execute()方法才创建线程。
第1-2个线程进入线程池创建
第3-5个线程进入阻塞队列
第6-8个线程会为他们创建新线程执行(直接运行线程6而非线程3)
第9个线程会被拒绝
总结来说:先到常驻线程,满了之后再到阻塞队列进行等待,阻塞队列满了之后,在往外扩容线程,扩容线程不能大于最大线程数。大于最大线程数和阻塞队列之和后,会执行拒绝策略。
具体的拒绝策略有:
- 抛异常-AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常运行
- 谁调用找谁-CallerRunsPolicy:“调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量
- 抛弃最久执行当前-DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中,尝试再次提交当前任务
- 不理不问-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】线程池相关推荐
- [转]Java并发编程:线程池的使用
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- Java并发编程:线程池的使用
在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...
- 【Java 并发编程】线程池机制 ( ThreadPoolExecutor 线程池构造参数分析 | 核心线程数 | 最大线程数 | 非核心线程存活时间 | 任务阻塞队列 )
文章目录 前言 一.ThreadPoolExecutor 构造参数 二.newCachedThreadPool 参数分析 三.newFixedThreadPool 参数分析 四.newSingleTh ...
- 【Java 并发编程】线程池机制 ( 线程池示例 | newCachedThreadPool | newFixedThreadPool | newSingleThreadExecutor )
文章目录 前言 一.线程池示例 二.newCachedThreadPool 线程池示例 三.newFixedThreadPool 线程池示例 三.newSingleThreadExecutor 线程池 ...
- 《转载》Python并发编程之线程池/进程池--concurrent.futures模块
本文转载自 Python并发编程之线程池/进程池--concurrent.futures模块 一.关于concurrent.futures模块 Python标准库为我们提供了threading和mul ...
- (转)Java并发编程:线程池的使用
背景:线程池在面试时候经常遇到,反复出现的问题就是理解不深入,不能做到游刃有余.所以这篇博客是要深入总结线程池的使用. ThreadPoolExecutor的继承关系 线程池的原理 1.线程池状态(4 ...
- Java并发编程一线程池简介
推荐:Java并发编程汇总 Java并发编程一线程池简介 为什么我们需要使用线程池? 我们知道线程是一种比较昂贵的资源,我们通过程序每创建一个线程去执行,其实操作系统都会对应地创建一个线程去执行我们的 ...
- Java并发编程一线程池的五种状态
推荐:Java并发编程汇总 Java并发编程一线程池的五种状态 原文地址 Java多线程线程池(4)–线程池的五种状态 正文 线程池的5种状态:Running.ShutDown.Stop.Tidyin ...
- Java并发编程:线程池
一.为什么使用线程池 使用线程的时候直接就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降 ...
- Java并发编程之线程池及示例
1.Executor 线程池顶级接口.定义方法,void execute(Runnable).方法是用于处理任务的一个服务方法.调用者提供Runnable 接口的实现,线程池通过线程执行这个 Runn ...
最新文章
- 使用nc传输文件和目录【转】
- PHP7Grafika,PHP图片处理库Grafika详细教程(3):图像属性处理
- 数据库系统概论:第八章 数据库编程
- java 配置文件加载_Java加载配置文件类
- hp laser103 属性没有配置项_(常见解决方法)UEditor报错“后端配置项没有正常加载,上传插件不能正常使用”...
- linux对目录进行操作,Linux 基础:对文件和目录进行操作的 Linux 和 Unix 命令 笔记...
- 共筑计算新生态 共赢数字新时代
- html xsl xml文件,用XSL显示XML文件看起来像HTML
- Fragment生命周期(转)
- 思科CDP/LLDP协议
- 计算机怎样更新卡驱动,显卡驱动怎么升级
- 股票大作手回忆录投机感悟
- php 写入exif,用PHP将EXIF写入JPG
- 手机点餐系统概述_廖师兄 微信点餐系统 springcloud学习笔记
- 2021鹏业安装算量软件常见问题整理(二十)
- 亚马逊正在逐渐压垮出版社,帮了世界一把
- GP232RL直接替代FT232RL串口芯片uart接口
- Jmeter源码分析(二)
- 关于QT中“崩溃”问题
- 当仁不让!一文看尽MWC舞台上的人工智能
热门文章
- 【linux】关于分析系统问题的前几分钟
- FCKeditor 2.6.4.1 初始化值不能显示中文问题
- 配置错误定义了重复的“system.web.extensions/scripting/scriptResourceHandler” 解决办法...
- xp与Vista双系统 相关问题
- Updater Application Block for .NET
- VC中退出应用程序-几种很有用的方法
- linux驱动篇之 driver_register 过程分析(二)bus_add_driver
- 《研磨设计模式》chap11 代理proxy模式
- [PSA]-PSA Certified简介
- 如何设计复用性较好的类?