并发编程之线程池的设计和原理

一、线程池

提前创建一系列的线程,保存在这个线程池中,有任务要执行的时候,从线程池中取出线程来执行。没有任务的时候,线程池放回去。

二、为什么要使用线程池

线程使用上的问题:

  • 线程的频繁创建 和 销毁

  • 线程的数量过多,会造成CPU资源的开销

    • 上下文切换(消耗CPU资源)

那么如何实现线程的复用呢? 池化技术

三、线程池的设计猜想

3.1线程池的设计思考?

需求: 实现线程的重复使用

分解:

  • 如何使用线程的复用?

    • 让线程实现复用的唯一方法,就是让线程不结束

      • 那如何让线程执行新的任务呢?也就是说,任务怎么给他执行?

        • [共享内存]-> List.add()

      • 线程一直处于运行状态,合理吗?

        • 有任务来的时候,执行,没有任务的时候,阻塞

结论: 通过阻塞队列的方式,来实现线程池中线程的复用

思考: 通过阻塞队列的方式,如果队列满了,可以阻塞主线程/生产者线程吗?显然不能,那么怎么办:

  • 1.增加消费者线程的数量(扩容) 也就是增加线程去执行任务,消费者多了,阻塞队列自然被消费的也快了,就不容易阻塞主线程了

  • 2.如果扩容解决不了问题,那只能采用拒绝策略

    • 报错

    • 直接丢弃这个任务

    • 直接普通线程调用task.run(直接重新开启一个线程)

    • 队列中头部的等待最久的任务丢弃,然后把当前任务添加到阻塞队列

    • 存储起来,后续等待空闲之后重试(自定义去完成)

结束的方法:让线程执行结束(run方法执行结束),也就是跳出while循环

3.2 线程池的核心参数

a.线程数量(核心线程数)  [初始化时创建]

b. 最大线程数 [还能够扩容多少个线程 与核心线程数的差]

c. 存活时间 [扩容的线程要有一个存活时间]

d. 存活时间单位

e.阻塞队列(使用哪一种阻塞队列)

f.线程工厂(生产线程的工厂)(有默认值)

h.阻绝策略(有默认值 默认抛出异常)

四、Java中提供的线程池

Exectors

  • newScheduledThreadPool 提供周期的线程池

  • newFixedThreadPool 固定线程数量

  • newSingleThreadExecutor 只有一个线程的线程池

  • newCachedThreadPool 可以缓存的线程池->理论上来说,有多少情况,就构建多少个线程

五、自定义线程池源码分析

线程池中的核心线程是延迟初始化的

  • 先初始化核心线程

  • 调用阻塞队列的方法,把task存进去

    • 如果true,说明当前的请求量不大,核心线程就可以搞定

    • false,增加工作线程(非核心线程)

      • 如果添加失败,说明当前的工作线程数量达到了最大的线程数,直接调用拒绝策略

    public void execute(Runnable command) {if (command == null)throw new NullPointerException();// 前3位记录运行状态  后29位记录线程数int c = ctl.get();// 1.判断当前工作线程数是否小于核心线程数(延迟初始化)if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))  // 添加工作线程,并执行任务return;c = ctl.get();}// 2.添加到阻塞队列中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);}// 3. 增加工作线程else if (!addWorker(command, false))reject(command);   // 4. 增加失败,则执行拒绝策略}

5.1 addWorker

    private boolean addWorker(Runnable firstTask, boolean core) {// 1. 修改工作线程记录数retry:for (;;) {int c = ctl.get();int rs = runStateOf(c);// Check if queue empty only if necessary.if (rs >= SHUTDOWN &&! (rs == SHUTDOWN &&firstTask == null &&! workQueue.isEmpty()))return false;for (;;) {int wc = workerCountOf(c);if (wc >= CAPACITY ||wc >= (core ? corePoolSize : maximumPoolSize))return false;if (compareAndIncrementWorkerCount(c))   //1.1 CAS操作进行修改break retry;c = ctl.get();  // Re-read ctlif (runStateOf(c) != rs)continue retry;// else CAS failed due to workerCount change; retry inner loop}}// 2. 创建线程并启动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 rs = runStateOf(ctl.get());if (rs < SHUTDOWN ||(rs == SHUTDOWN && firstTask == null)) {if (t.isAlive()) // precheck that t is startablethrow new IllegalThreadStateException();workers.add(w);int s = workers.size();if (s > largestPoolSize)largestPoolSize = s;workerAdded = true;}} finally {mainLock.unlock();}if (workerAdded) {t.start();workerStarted = true;}}} finally {// 3. 添加失败 则回滚if (! workerStarted)addWorkerFailed(w);}return workerStarted;}

分析: addWorker主要做3件事,.a.使用CAS操作修改原子值中的工作线程数  b.将任务放到Worker对象中,并启动线程 c.如果线程启动失败,则回滚。线程启动后,则自动调用Worker对象中的run方法。

5.2 runWorker

    final void runWorker(Worker w) {Thread wt = Thread.currentThread();Runnable task = w.firstTask;w.firstTask = null;w.unlock(); // allow interruptsboolean completedAbruptly = true;try {// 1. while循环保证当前线程不结束,直到task为空while (task != null || (task = getTask()) != null) {// 2.这里是因为表示当前线程正在运行一个任务,其它地方要执行shutdown 你要等我执行结束w.lock(); // worker继承了AQS  实现了互斥锁if ((runStateAtLeast(ctl.get(), STOP) ||(Thread.interrupted() &&runStateAtLeast(ctl.get(), STOP))) &&!wt.isInterrupted())wt.interrupt();  // 是否应该中断 在gettask中处理try {// 空实现,方便扩展beforeExecute(wt, task);Throwable thrown = null;try {task.run();} catch (RuntimeException x) {thrown = x; throw x;} catch (Error x) {thrown = x; throw x;} catch (Throwable x) {thrown = x; throw new Error(x);} finally {// 空实现,方便扩展afterExecute(task, thrown);}} finally {task = null;w.completedTasks++;w.unlock();}}completedAbruptly = false;} finally {processWorkerExit(w, completedAbruptly);}}

5.3 getTask

    private Runnable getTask() {boolean timedOut = false; // Did the last poll() time out?for (;;) {int c = ctl.get();int rs = runStateOf(c);//1. 判断如果线程池已经结束,直接返回Null,需要清理掉所有的工作线程if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {decrementWorkerCount();return null;}int wc = workerCountOf(c);// 2. 是否允许超时   allowCoreThreadTimeOut 为true 也就是说设置为true 核心线程也 // 可以被销毁// 或者工作线程数大于核心线程数boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;if ((wc > maximumPoolSize || (timed && timedOut))&& (wc > 1 || workQueue.isEmpty())) {if (compareAndDecrementWorkerCount(c))   // cas操作减少工作线程return null;continue;}try {//* 执行任务Runnable r = timed ?// 超时阻塞workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :// 如果阻塞队列为空,则会阻塞在这个地方workQueue.take();if (r != null)return r;timedOut = true;} catch (InterruptedException retry) {timedOut = false;}}}

分析: getTask中从队列中取出任务并执行。注意:allowCoreThreadTimeOut 设置为true,则核心线程也可以被销毁

5.4 拒绝策略

a. AbortPolicy(抛出异常)

        // 报错 抛出异常public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {throw new RejectedExecutionException("Task " + r.toString() +" rejected from " +e.toString());}

b. CallerRunsPolicy (创建一个普通线程,并执行)

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {r.run();}}

c. DiscardOldestPolicy (从队列头部抛弃一个,执行当前的)

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {e.getQueue().poll();e.execute(r);}}

d. DiscardPolicy (什么也不做)

   public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}

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

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

相关文章

政安晨:【Keras机器学习示例演绎】(二十六)—— 图像相似性搜索的度量学习

目录 概述 设置 数据集 嵌入模型 测试 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: TensorFlow与Keras机器学习实战 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 本文…

基于Pytorch深度学习——多层感知机

本文章来源于对李沐动手深度学习代码以及原理的理解&#xff0c;并且由于李沐老师的代码能力很强&#xff0c;以及视频中讲解代码的部分较少&#xff0c;所以这里将代码进行尽量逐行详细解释 并且由于pytorch的语法有些小伙伴可能并不熟悉&#xff0c;所以我们会采用逐行解释小…

Word域代码学习(简单使用)-【SEQ】

Word域代码学习(简单使用)-【SEQ】 快捷键 序号快捷键操作1 Ctrl F9 插入域代码花括号2 F9 显示域代码结果3 Shift F9 切换为域代码4 Windows Alt F9 切换全部域代码 域代码说明 域代码不区分大小写在word中&#xff0c;依次选择插入➡文档部件➡域即可选择插入…

Linux 学习 --- 编辑 vi 命令

1、vi 基本概念&#xff08;了解&#xff09; 基本上 vi 可以分为三种状态&#xff0c;分别是命令模式 (command mode)、插入模式 (Insert mode) 和底行模式 (last line mode)&#xff0c;各模式的功能区分如下: 命令行模式 command mode&#xff09;  控制屏幕光标的移动&a…

PotatoPie 4.0 实验教程(31) —— FPGA实现摄像头图像高斯滤波

什么是高斯滤波 高斯滤波是一种常见的图像处理技术&#xff0c;用于去除图像中的噪声和平滑图像。它的原理基于统计学中的高斯分布&#xff08;也称为正态分布&#xff09;。 在高斯滤波中&#xff0c;一个二维的高斯核函数被用来对图像中的每个像素进行加权平均。这个高斯核…

jvm 马士兵 01

01.JVM是什么 JVM是一个跨平台的标准 JVM只识别class文件&#xff0c;符合JVM规范的class文件都可以被识别

AI智能名片商城小程序:引领企业迈向第三增长极

随着数字化浪潮的席卷&#xff0c;私域流量的重要性逐渐凸显&#xff0c;为企业增长提供了全新的动力。在这一背景下&#xff0c;AI智能名片商城系统崭露头角&#xff0c;以其独特的优势&#xff0c;引领企业迈向第三增长极。 私域流量的兴起&#xff0c;为企业打开了一扇新的销…

【codeforces】Immobile Knight

Immobile Knight 我感觉自己不太适合写codeforces&#xff0c;简单题也比较考验思维&#xff0c;当时这题看了半天以为是搜索&#xff0c;写了20分钟暴力交了&#xff0c;还好对的&#xff0c;20个人19个人5分钟不到速通第一题&#xff0c;唯留我一人在第一题凌乱。下来看看这…

深度学习中的归一化:BN,LN,IN,GN的优缺点

目录 深度学习中归一化的作用常见归一化的优缺点 深度学习中归一化的作用 加速训练过程 归一化可以加速深度学习模型的训练过程。通过调整输入数据的尺度&#xff0c;归一化有助于改善优化算法的收敛速度。这是因为归一化后的数据具有相似的尺度&#xff0c;使得梯度下降等优化…

密码学基础练习五道 RSA、elgamal、elgamal数字签名、DSA数字签名、有限域(GF)上的四则运算

1.RSA #include <stdlib.h>#include <stdio.h>#include <string.h>#include <math.h>#include <time.h>#define PRIME_MAX 200 //生成素数范围#define EXPONENT_MAX 200 //生成指数e范围#define Element_Max 127 //加密单元的…

dockerfile 搭建lamp 实验模拟

一 实验目的 二 实验 环境 1, 实验环境 192.168.217.88一台机器安装docker 并做mysql nginx php 三台容器 2&#xff0c; 大致框架 3&#xff0c; php php:Nginx服务器不能处理动态页面&#xff0c;需要由 Nginx 把动态请求交给 php-fpm 进程进行解析 php有三…

LT6911UXB HDMI2.0 至四端口 MIPI DSI/CSI,带音频 龙迅方案

1. 描述LT6911UXB 是一款高性能 HDMI2.0 至 MIPI DSI/CSI 转换器&#xff0c;适用于 VR、智能手机和显示应用。HDMI2.0 输入支持高达 6Gbps 的数据速率&#xff0c;可为4k60Hz视频提供足够的带宽。此外&#xff0c;数据解密还支持 HDCP2.2。对于 MIPI DSI / CSI 输出&#xff0…

van-cascader(vant2)异步加载的bug

问题描述&#xff1a;由于一次性返回所有的级联数据的话&#xff0c;数据量太大&#xff0c;接口响应时间太久&#xff0c;因此采用了异步加载的方案&#xff0c;看了vant的官方示例代码&#xff0c;照着改了下&#xff0c;很轻松地实现了功能。正当我感叹世界如此美好的时候&a…

【C++ —— 多态】

C —— 多态 多态的概念多态的定义和实现多态的构成条件虚函数虚函数的重写虚函数重写的两个例外协变&#xff1a;析构函数的重写 C11 override和final重载、覆盖(重写)、隐藏(重定义)的对比 抽象类概念接口继承和实现继承 多态的继承虚函数表多态的原理动态绑定和静态绑定 单继…

数据库(MySQL)基础:多表查询(一)

一、多表关系 概述 项目开发中&#xff0c;在进行数据库表结构设计时&#xff0c;会根据业务需求及业务模块之间的关系&#xff0c;分析并设计表结构&#xff0c;由于业务之间相互关联&#xff0c;所以各个表结构之间也存在着各种联系&#xff0c;基本上分为三种&#xff1a;…

OceanBase开发者大会实录-陈文光:AI时代需要怎样的数据处理技术?

本文来自2024 OceanBase开发者大会&#xff0c;清华大学教授、蚂蚁技术研究院院长陈文光的演讲实录—《AI 时代的数据处理技术》。完整视频回看&#xff0c;请点击这里&#xff1e;> 大家好&#xff0c;我是清华大学、蚂蚁技术研究院陈文光&#xff0c;今天为大家带来《AI 时…

【C语言】atoi和atof函数的使用

人生应该树立目标&#xff0c;否则你的精力会白白浪费。&#x1f493;&#x1f493;&#x1f493; 目录 •&#x1f319;知识回顾 &#x1f34b;知识点一&#xff1a;atoi函数的使用和实现 • &#x1f330;1.函数介绍 • &#x1f330;2.代码演示 • &#x1f330;3.atoi函数的…

Flask框架进阶-Flask流式输出和受访配置--纯净详解版

Flask流式输出&#x1f680; 在工作的项目当中遇到了一种情况&#xff0c;当前端页面需要对某个展示信息进行批量更新&#xff0c;如果直接将全部的数据算完之后&#xff0c;再返回更新&#xff0c;则会导致&#xff0c;前端点击刷新之后等待时间过长&#xff0c;开始考虑到用进…

liceo靶机复现

liceo-hackmyvm 靶机地址&#xff1a;https://hackmyvm.eu/machines/machine.php?vmLiceo 本机环境&#xff1a;NAT模式下&#xff0c;使用VirtualBox 信息收集&#xff1a; 首先局域网内探测靶机IP 发现IP为10.0.2.4 开启nmap扫描一下看看开了什么端口 扫描期间看一下web页…

[蓝桥杯2024]-PWN:fd解析(命令符转义,标准输出重定向,利用system(‘$0‘)获取shell权限)

查看保护 查看ida 这里有一次栈溢出&#xff0c;并且题目给了我们system函数。 这里的知识点没有那么复杂 方法一&#xff08;命令转义&#xff09;&#xff1a; 完整exp&#xff1a; from pwn import* pprocess(./pwn) pop_rdi0x400933 info0x601090 system0x400778payloa…