ThreadPoolExcuter源码阅读

线程池的基本参数

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), handler);
}

主要是
核心线程数:可以一直存活的工作线程
最大线程数:当线程队列满的时候增加线程数,超过核心线程数的新增的为非核心线程,超过固定时长没任务执行就进行销毁
非核心线程的空闲时长:非核心线程超过多久进行销毁
工作队列:当核心线程都在工作的时候,新来的任务放入工作队列,等待执行
拒绝策略:核心队列满了,最大线程数也达到了,新来的任务执行拒绝策略
还有可以自定义ThreadFactory,控制线程的创建,这里没特殊需求,使用的是默认的线程工厂

线程池的拒绝策略

可以自定义线程的拒绝策略,默认提供拒绝策略主要分为四种

  1. CallerRunsPolicy让主线程进行执行
  2. AbortPolicy拒绝任务,抛出异常RejectedExecutionException
  3. DiscardPolicy 直接丢掉这个任务
  4. DiscardOldestPolicy 直接丢掉最早的任务

一般情况下可以满足使用,如有特殊情况,可以进行自定义拒绝策略,实现接口RejectedExecutionHandler

线程池执行过程

    public void execute(Runnable command) {if (command == null)throw new NullPointerException();// 这里的c 是定义的AtomicInteger的一个成员变量,使用29位来表示线程数,剩下3位表示线程池的//状态,所以最大支持的线程数(2^29)-1int c = ctl.get();// 如果当前线程数小于核心线程数if (workerCountOf(c) < corePoolSize) {// 执行添加工作线程if (addWorker(command, true))return;c = ctl.get();}// 这里经过上面,证明没有添加核心线程,如果线程池在运行状态,添加到工作队列里面if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();if (! isRunning(recheck) && remove(command))reject(command);// 这里判断如果工作线程数为0(因为核心线程数允许为0),是要添加一个线程的,else if (workerCountOf(recheck) == 0)addWorker(null, false);}//如果没添加到队列,应该队列满了,直接添加非核心线程,如果添加失败走拒绝策略else if (!addWorker(command, false))reject(command);}

线程池的几种状态
RUNNING: 接受新任务并处理排队的任务
SHUTDOWN: 不接受新任务,但是处理队列里面的人物
STOP: 不接受新任务,不处理排队的任务,并中断正在进行的任务
TIDYING: 所有任务都已终止,workerCount为零,转换到状态TIDYING的线程将运行terminated()
TERMINATED: terminated()钩子方法完成时

分析下addWorker方法做了什么

    private boolean addWorker(Runnable firstTask, boolean core) {retry:for (int c = ctl.get();;) {// Check if queue empty only if necessary.if (runStateAtLeast(c, SHUTDOWN)&& (runStateAtLeast(c, STOP)|| firstTask != null|| workQueue.isEmpty()))return false;for (;;) {// 判断能不能添加线程,是不是超过设置的核心线程,最大线程数if (workerCountOf(c)>= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))return false;// 增加线程数量,跳出循环if (compareAndIncrementWorkerCount(c))break retry;c = ctl.get();  // Re-read ctl// 继续添加if (runStateAtLeast(c, SHUTDOWN))continue retry;// else CAS failed due to workerCount change; retry inner loop}}boolean workerStarted = false;boolean workerAdded = false;Worker w = null;try {w = new Worker(firstTask);final Thread t = w.thread;if (t != null) {final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {// Recheck while holding lock.// Back out on ThreadFactory failure or if// shut down before lock acquired.int c = ctl.get();if (isRunning(c) ||(runStateLessThan(c, STOP) && firstTask == null)) {if (t.getState() != Thread.State.NEW)throw new IllegalThreadStateException();workers.add(w);workerAdded = true;int s = workers.size();if (s > largestPoolSize)largestPoolSize = s;}} finally {mainLock.unlock();}if (workerAdded) {// 启动线程t.start();workerStarted = true;}}} finally {if (! workerStarted)addWorkerFailed(w);}return workerStarted;}

简单总结下,判断下添加工作线程,线程池是不是运行状态,是不是达到了设置的核心线程和最大线程限制,不符合条件的直接返回添加失败,符合条件的进行添加,如果添加成功,跳出循环,没添加成功就继续循环cas添加。
检查通过并且增加数量之后,开始实际创建Worker,然后归入workers进行管理,处理完之后调用线程的start方法,线程的start方法最后会调用jni新开线程,执行到run()方法,那么就去看下run方法的执行逻辑,run里面比较简单,直接调用的 runWorker(this);

runWoker 是怎么执行的

    final void runWorker(Worker w) {Thread wt = Thread.currentThread();Runnable task = w.firstTask;w.firstTask = null;w.unlock(); // allow interruptsboolean completedAbruptly = true;try {// 获取task,如果fisrtTask为空,就执行getTask进行获取while (task != null || (task = getTask()) != null) {w.lock();// If pool is stopping, ensure thread is interrupted;// if not, ensure thread is not interrupted.  This// requires a recheck in second case to deal with// shutdownNow race while clearing interrupt// 判断下线程池状态和线程状态,是不是进行了打断操作if ((runStateAtLeast(ctl.get(), STOP) ||(Thread.interrupted() &&runStateAtLeast(ctl.get(), STOP))) &&!wt.isInterrupted())wt.interrupt();try {// 执行任务之前的扩展操作,beforeExecute(wt, task);try // 执行任务task.run();// 任务执行之后的扩展操作afterExecute(task, null);} catch (Throwable ex) {afterExecute(task, ex);throw ex;}} finally {task = null;w.completedTasks++;w.unlock();}}completedAbruptly = false;} finally {processWorkerExit(w, completedAbruptly);}}

总结下就是循环获取任务进行执行,执行判断下线程池的状态和线程的状态,然后可以执行下扩展的任务执行前后需要执行的操作,发生异常执行worker退出操作

核心线程和非核心线程是怎么区分管理的

这个主要就在getTask方法的管理了,task究竟是怎么进行获取的呢

private Runnable getTask() {boolean timedOut = false; // Did the last poll() time out?// 死循环获取任务for (;;) {int c = ctl.get();// Check if queue empty only if necessary.if (runStateAtLeast(c, SHUTDOWN)&& (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {decrementWorkerCount();return null;}int wc = workerCountOf(c);// Are workers subject to culling?// 判断是不是有超时时间,核心线程是不是有超时时间boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;// 判断线程是不是具备销毁条件,超过最大线程数,或者具备超时时间并且超过的// 减去线程数if ((wc > maximumPoolSize || (timed && timedOut))&& (wc > 1 || workQueue.isEmpty())) {if (compareAndDecrementWorkerCount(c))return null;continue;}try {// 这里就是区分是不是需要超时时间的,主要区分于核心线程和非核心线程,// 常驻的线程直接就是take()死等// 需要具备超时时间的线程就使用workQueue的poll来设置超时时间,如果超时了// 下次循环判断出来就执行上面的减去线程的操作了Runnable r = timed ?workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :workQueue.take();if (r != null)return r;timedOut = true;} catch (InterruptedException retry) {timedOut = false;}}}

总结下,就是线程是不是常驻线程,会不会超时,怎么进行控制的,主要就是通过getTask里面进行区别对待的,使用工作队列的take()和poll()来进行区别设置超时时间,超时的再执行消除,然后返回的task就是null,由上层执行销毁线程的操作

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/17283.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MongDB文档--架构体系

阿丹&#xff1a; 在开始学习先了解以及目标知识的架构体系。就能事半功倍。 架构体系 MongoDB的架构体系由以下几部分组成&#xff1a; 存储结构&#xff1a;MongoDB采用文档型存储结构&#xff0c;一个数据库包含多个集合&#xff0c;一个集合包含多个文档。存储形式&#…

前端Vue入门-day06-路由进阶

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 路由的封装抽离 声明式导航 导航链接 两个类名 自定义高亮类名 跳转传参 1. 查询参数传参 2. 动态…

opencv基础函数使用

文章目录 前言一、Mat类二、Vec3b三、Scalar函数四、imread函数五、imshow函数六、imwrite函数总结前言 本篇文章带大家来学习一下opencv基础函数的使用。 一、Mat类 Mat类是OpenCV中最常用的数据结构之一,用于表示和操作图像和矩阵数据。它提供了一个多维数组,用于存储像…

Socket 前端项目结构搭建

npm install socket.io-client --savenpm install element-plus --savenpm install vue-router4.0.12 --save简单的页面搭建 聊天系统登录前端实现 登录模板 <template><div class"login-container"><el-form ref"form" :model"fo…

IDEA快捷键大全

编辑与导航快捷键&#xff1a; Ctrl S: 保存当前文件Ctrl Z: 撤销Ctrl Y: 重做&#xff08;Redo&#xff09;Ctrl X: 剪切Ctrl C: 复制Ctrl V: 粘贴Ctrl D: 复制当前行或选定的区域Ctrl Delete: 删除光标后的单词Ctrl Backspace: 删除光标前的单词Ctrl Shift 上箭头…

使用 OpenCV 和深度学习对黑白图像进行着色

在本文中,我们将创建一个程序将黑白图像(即灰度图像)转换为彩色图像。我们将为此程序使用 Caffe 着色模型。您应该熟悉基本的 OpenCV 功能和用法,例如读取图像或如何使用 dnn 模块加载预训练模型等。现在让我们讨论实现该程序所遵循的过程。 给定一张灰度照片作为输入,本文…

Maven 打包项目后,接口识别中文乱码

背景 项目在Idea里面运行&#xff0c;调用接口发送中文消息正常&#xff0c;用Maven打包项目后&#xff0c;运行jar包&#xff0c;调用接口发送中文出现乱码。 解决方法 1.Idea编译配置 2.如果更改了上述配置之后还是没有效果&#xff0c;则在运行jar包的前面加上 -Dfile.en…

100天精通Python(可视化篇)——第96天:Pyecharts绘制多种炫酷箱形图参数说明+代码实战

文章目录 专栏导读1. 箱形图介绍1&#xff09;箱形图介绍2&#xff09;怎么看箱型图&#xff1f;3&#xff09;解释说明 2. 普通箱型图3. 水平箱型图4. 群组箱型图5. 带异常点的箱型图书籍推荐 专栏导读 &#x1f525;&#x1f525;本文已收录于《100天精通Python从入门到就业…

rust 如何定义[u8]数组?

在Rust中&#xff0c;有多种方式可以定义 [u8] 数组。以下是一些常见的方式&#xff1a; 使用数组字面量初始化数组&#xff1a; let array: [u8; 5] [1, 2, 3, 4, 5];使用 vec! 宏创建可变长度的数组&#xff1a; let mut vec: Vec<u8> vec![1, 2, 3, 4, 5];使用 v…

适配器模式与装饰器模式对比分析:优雅解决软件设计中的复杂性

适配器模式与装饰器模式对比分析&#xff1a;优雅解决软件设计中的复杂性 在软件设计中&#xff0c;我们常常面临着需要将不同接口或类协调工作的情况&#xff0c;同时还要满足灵活性和可扩展性的需求。为了应对这些挑战&#xff0c;适配器模式和装饰器模式应运而生&#xff0c…

IDEA Debug小技巧 添加减少所查看变量、查看不同线程

问题 IDEA的Debug肯定都用过。它下面显示的变量&#xff0c;有什么门道&#xff1f;可以增加变量、查看线程吗&#xff1f; 答案是&#xff1a;可以。 演示代码 代码如下&#xff1a; package cn.itcast.attempt.threadAttempt.attempt2;public class Test {public static …

GFS分布式文件系统

目录 一、GlusterFS简介 二、GlusterFS特点 1.扩展性和高性能 2.高可用性 3.全局统一命名空间 4.弹性卷管理 5.基于标准协议 三、GlusterFS 术语 1.Brick&#xff08;存储块&#xff09; 2.Volume&#xff08;逻辑卷&#xff09; 3.FUSE 4.VFS 5.Glusterd&#xf…

day48-ajax+SSM分页

AjaxSSM分页 非分页版controller及html&#xff1a; 分页模糊查询controller&#xff1a; Postman测试&#xff08;无网页&#xff09;&#xff1a; 分页网页&#xff1a; 分页网页中添加模糊查询&#xff1a; 分页网页中实现添加功能&#xff1a; &#xff08;1&am…

Flutter 状态栏完美攻略

1. 沉浸式状态栏 Scaffold(extendBodyBehindAppBar: true,appBar: AppBar(toolbarHeight: 0,),body: Container(color:Colors.red) ) 2. 状态栏的背景颜色 Scaffold(appBar: AppBar(backgroundColor: Colors.transparent,),body: Container(color:Colors.red) ) 3. 状态栏的…

RabbitMQ 教程 | 第4章 RabbitMQ 进阶

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的编码爱好者 大家好&#xff0c;我是 DevO…

【深度学习Week3】ResNet+ResNeXt

ResNetResNeXt 一、ResNetⅠ.视频学习Ⅱ.论文阅读 二、ResNeXtⅠ.视频学习Ⅱ.论文阅读 三、猫狗大战Lenet网络Resnet网络 四、思考题 一、ResNet Ⅰ.视频学习 ResNet在2015年由微软实验室提出&#xff0c;该网络的亮点&#xff1a; 1.超深的网络结构&#xff08;突破1000层&…

b 树和 b+树的理解

项目场景&#xff1a; 图灵奖获得者&#xff08;Niklaus Wirth &#xff09;说过&#xff1a; 程序 数据结构 算法&#xff0c; 也就说我们无时无刻 都在和数据结构打交道。 只是作为 Java 开发&#xff0c;由于技术体系的成熟度较高&#xff0c;使得大部分人认为&#xff1…

留存测试数据,Apipost接口用例详解

接口用例可以在不影响源接口数据的情况下对接口添加多个用例&#xff0c;方便测试并保存测试数据。 创建用例 左侧目录选择接口后进入接口用例页面&#xff0c;点击添加用例 在弹出窗口中修改各种参数。如登录接口&#xff0c;可修改用户名为空&#xff0c;并添加断言。 执行…

数据湖如何为企业带来9%的高增长?可否取代数据仓库?

什么是数据湖&#xff1f; 数据湖是一个集中的存储库&#xff0c;允许您以任何规模存储所有结构化和非结构化数据。您可以按原样存储数据&#xff0c;而不必首先构造数据&#xff0c;并运行不同类型的分析—从仪表板和可视化到大数据处理、实时分析和机器学习&#xff0c;以指…

css滤镜:drop-shadow

一、用法 drop-shadow( offset-x offset-y blur-radius spread-radius color ) offset-x&#xff1a;此参数设置图像的水平偏移。正值将创建右侧的偏移量&#xff0c;负值将创建左侧的偏移量。offset-y&#xff1a;此参数设置图像的垂直偏移。正值创建到底部的偏移量&#xff…