【多线程和并发】Java中的线程池的实现原理
Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行的程序都可以使用线程池。
合理使用线程池能带来三个好处:
- 降低资源消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗;
- 提高响应速度,当任务到达时,任务可以不需要等到线程创建就能立即执行;
- 提高线程的可管理性,线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配,调优和监控。
线程池的实现原理
当提交一个任务时,线程池的处理流程如下
很清晰的一幅图,简洁明了
- 我们提交一个任务到线程池时,线程池首先判断当前核心线程池是否已满,如果没有满的话,那么就直接创建一个新的线程来执行任务;如果核心线程池已满的话那么就去判断队列是否已满;
- 如果工作队列没有满的话,那么就将这个任务存储到工作队列中进行等待;如果没满的话,那么就去判断线程池是否已满;
- 如果线程池没有满的话,同样创建一个新的线程去执行任务;相反,如果线程池满的话那么就去按照饱和策略处理无法执行的任务。
ThreadPoolExcutor执行excute()方法的示意图
ThreadPoolExcute执行excute方法分下面四种情况:
- 如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步需要获取全局锁);
- 如果运行的线程等于corePoolSize,则将任务加入BlockingQueue;
- 如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(同上,执行这一步需要获取全局锁);
- 如果创建新线程将使当前运行的线程 超出maximumPoolSize,任务将被拒绝,并且调用RejectedExecutionHandler.rejectedExecution()方法
ThreadPoolExecutor采取上述步骤的总体设计思路,是为了在执行execute()方法时,尽可能地避免获取全局锁(那将回事一个严重的可伸缩瓶颈)。在ThreadPoolExecutor完成预热之后(当前运行的线程数大于等于corePoolSize),几乎所有的execute()方法调用都是执行步骤2,而步骤2不需要获取全局锁。
源码分析
public void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();/*** 如果运行的线程小于corePoolSize,则尝试用给定的命令作为第一个任务启动一个新线程。* 对addWorker的调用原子性地检查runState和workerCount,因此可以通过返回false来防止错误警报,因为错误警报会在不应该添加线程的时候添加线程。*/if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();}// 线程池处于RUNNING状态,并将任务放入workQueue队列if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();// 需要再次检查是否应该添加一个线程(因为自上次检查以来已有的线程已经死亡),或者池在进入这个方法后关闭。if (! isRunning(recheck) && remove(command))reject(command);// 重新检查状态,如果必要的话,如果停止,则回滚队列;如果没有,则启动一个新线程。else if (workerCountOf(recheck) == 0)addWorker(null, false);}// 如果队列满了,则尝试添加新线程。如果它失败了,则执行线程池的饱和策略。else if (!addWorker(command, false))reject(command);}
工作线程:线程池创建线程时,会将线程粉装成工作线程Worker,Worker在执行完任务后,还会循环获取工作队列里的任务来执行。我们可以从Worker类的run()方法里看到。
public void run(){try{Runnable task = firstTask;firstTask = null;while(task != null || (task = getTask()) != null){runTask(task);task = null;}} finally {workerDone(this);}
}
ThreadPoolExecutor中线程执行任务的示意图如图
线程池中的线程执行任务分两种情况,如下:
- 在execute()方法中创建一个线程时,会让这个线程执行当前任务;
- 这个线程执行完图中1的任务后,会反复从BlockingQueue获取任务来执行。
————《Java并发编程的艺术》 方腾飞 魏鹏 程晓明 著
【多线程和并发】Java中的线程池的实现原理相关推荐
- 四十七、面试前,必须搞懂Java中的线程池ThreadPoolExecutor(上篇)
@Author:Runsen @Date:2020/6/9 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏艰 ...
- 万字图文 | 学会Java中的线程池,这一篇也许就够了!
来源:一枝花算不算浪漫 线程池原理思维导图.png 前言 Java中的线程池已经不是什么神秘的技术了,相信在看的读者在项目中也都有使用过.关于线程池的文章也是数不胜数,我们站在巨人的肩膀上来再次梳理一 ...
- JAVA中创建线程池的五种方法及比较
之前写过JAVA中创建线程的三种方法及比较.这次来说说线程池. JAVA中创建线程池主要有两类方法,一类是通过Executors工厂类提供的方法,该类提供了4种不同的线程池可供使用.另一类是通过Thr ...
- 多线程线程池的实现java_如何在Java中实现线程池
多线程线程池的实现java 线程是独立程序的执行路径. 在java中,每个线程都扩展java.lang.Thread类或实现java.lang.Runnable. 多线程是指在一个任务中同时执行两个或 ...
- 如何在Java中实现线程池
线程是独立程序的执行路径. 在java中,每个线程都扩展java.lang.Thread类或实现java.lang.Runnable. 多线程是指在一个任务中同时执行两个或多个线程.在多线程中,每个任 ...
- java中的线程池有哪些,分别有什么作用?
阅读完本篇文章会知道如下三点: 1.进程-线程简单介绍 2.java的线程池是什么,有哪些类型,作用分别是什么 3.使用线程池的优点 1.进程-线程的简单介绍 进程 什么是进程呢? 进程是计算机中的程 ...
- Java多线程编程(1)--Java中的线程
一.程序.进程和线程 程序是一组指令的有序集合,也可以将其通俗地理解为若干行代码.它本身没有任何运行的含义,它只是一个静态的实体,它可能只是一个单纯的文本文件,也有可能是经过编译之后生成的可执行文 ...
- 深入理解java中的线程池
线程池中各个参数的含义 corePoolSize: 核心池的大小,这个参数跟线程池的实现原理有非常大的关系.**在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行 ...
- 【高并发】java中的线程池 ThreadPoolExecutor
文章目录 1.概述 1.2 参数顺序 2. 案例 2.1 线程池使用的简单示例 2.2 线程池使用的简单示例 3. 线程池中常见5种工作队列 3.1 SynchronousQueue队列的线程池 3. ...
最新文章
- 突破:量子计算机首次实现简化逻辑门
- C语言函数调用过程的汇编分析(停更)
- ubuntu18.04利用fdisk找到磁盘空闲区,新建分区,挂载
- ajax后台重定向会返回什么_跳转,AJAX返回和重定向
- C++ 中export 关键字的尴尬处境
- SAP MM ME51N 创建采购申请单据时候永远取物料主数据基本计量单位
- SQL Server 中系统表的作用
- Mac如何打开CAJ格式的文件?
- ESP8266(3)
- 二进制 八进制 十进制 十六进制
- 嵌入式C语言static关键字
- 存在感应雷达模块,LED灯感应控制,微波雷达技术应用
- 使用ingress暴露kubernetes集群内部的pod服务
- 高速串行总线设计基础(七)揭秘SERDES高速面纱之时钟校正与通道绑定技术
- chromecast网络访问受限
- 向阅读致敬!微信读书产品设计策略推导
- docker容器虚拟技术
- 如何提升自身能力?不再平庸
- 朴实无华,图解快排,多语言实现。(PS:还有宝藏资料)
- HFM-合并应用程序迁移(COPY APPLICATION)