小白科普:线程和线程池

第九章:C++ 简易的线程池 本文介绍了C++线程池的实现方案,重点讲解了其核心设计思想和技术实现。线程池通过预先创建一组工作线程实现线程复用,采用生产者-消费者模式解耦任务提交与执行,并通过双优先级队列(普通任务+优先任务)优化任务调度。文章详细剖析了线程池的架构设计,包括资源管理、任务调度、线程安全等关键机制,并展示了如何利用现代C++特性(如模板编程、完美转发、future/promise等)实现一个高性能线程池。该实现支持任意可调用对象的任务提交,通过RAII管理资源生命周期,确保线程安全和异常安全,适用于服务器等需要高并发的场 阅读详情

软件大师又要给弟子开小灶了,这次是线程和线程池。

软件大师正在闭目修炼, 最小的一名弟子慢慢走了进来。

640?wx_fmt=png

大师,最近我在学习线程,有很多迷惑的地方。

说来听听,让为师给你排解一下。

640?wx_fmt=png

640?wx_fmt=png

第一个问题问题就是为什么要多线程啊, 我看了操作系统中的多进程管理,不是挺好的吗? 多线程似乎没有必要啊!

不错,多问问为什么,总是有好处的。所谓线程,就是程序代码的执行,一个进程至少得有一个线程,要不然,这个进程怎么运行? 对吧?

640?wx_fmt=png

640?wx_fmt=png

这个我理解。

以你常用的Word为例,假设这个进程没有多线程(或者说它只有一个线程), 如果它有个定时保存文档的功能,你想象下,当这个自动保存的功能在运行的时候,你还能继续输入文字吗?

640?wx_fmt=png

640?wx_fmt=png

不能

对,这就是问题所在了,单线程只能干一件事情,无法并发和并行。直接导致用户体验不好。CPU快速的运算能力,还有多核,就被浪费了。

640?wx_fmt=png

640?wx_fmt=png

那我完全可以用多进程来处理啊, 一个进程来接受用户输入文字,另外一个进程来自动保存。

是可以这么做,但进程是个重量级的家伙,并且进程之间是隔离的,他们要想共享数据,例如被编辑的文件内容,那是非常麻烦的。

640?wx_fmt=png

640?wx_fmt=png

原来如此。

古人云:进程是拥有资源的基本单位, 线程是CPU调度的基本单位,这句话你理解吗?

640?wx_fmt=png

640?wx_fmt=png

不是特别明白......

举个例子,有两个进程,一个是Word, 一个是QQ音乐。 Word 进程打开了文件,这是它的资源,QQ音乐打开了Socket,这也是它的资源。

假设Word有两个线程:T1负责接受用户的文字输入,T2 负责自动保存。

QQ音乐也有两个线程:T3负责从Socket中读取数据,T4负责对音乐数据进行解码。

操作系统在做调度的时候,基本单位不是Word,QQ音乐这样的进程,而是T1, T2,T3,T4这些线程。明白了吗?

640?wx_fmt=png

640?wx_fmt=png

640?wx_fmt=png

(点头)原来每个线程执行的都是进程代码的某个片段啊! 对了,我听师兄们在讨论什么Java多线程编程,说是很复杂,但是他们从来没提到过Java 多进程编程,这是怎么回事啊?

不错,你的师兄们都没有想到这个问题,看来你已经开始思考了。我先问你,你写的Java程序是不是运行在JVM中? 对操作系统来说,JVM是个什么东西?

640?wx_fmt=png

640?wx_fmt=png

嗯... JVM其实就是java.exe运行起来,那它肯定是个进程了。

那在一个进程中还能进行多进程编程吗?

640?wx_fmt=png

640?wx_fmt=png

(恍然大悟)奥,那是肯定不行了,Java程序运行在JVM当中, JVM这个进程其实就是他们的容器。 我听说Python, Ruby 等动态语言也都有虚拟机, 这么说他们也可以进行多线程编程了。

是啊,虚拟机是个好东西,你们真是遇上了好时候啊, 不用再费劲心机去操作内存。这虚拟机还能屏蔽操作系统的差异,你写的程序可以在任意的支持该语言虚拟机的操作系统中运行。 可移植性很重要,要不然,你在Mac/Windows上开发的程序怎么能不加修改地放到Linux上去运行呢?

640?wx_fmt=png

640?wx_fmt=png

我在Java 中创建了一个Thread对象,为什么要调用start方法才能启动线程? 为什么不能直接调用run方法呢?

你要是直接调用run()方法,会是什么效果?

640?wx_fmt=png

640?wx_fmt=png

就是用当前线程去执行一个普通函数而已,根本没有什么新线程创建出来。

这就对了,你想创建一个新的线程出来,肯定得有准备工作啊,设置好这个线程的上下文,比如这个线程的栈(用于函数调用),线程的状态,这个线程的PC(Program Counter)等等一系列信息以后,这个线程才可以被调度, 一旦被调度,就会执行那个run()方法了。

640?wx_fmt=png

640?wx_fmt=png

明白了大师,还有一个问题,既然线程是属于进程的,可以共享进程的资源, 那创建一个线程应该很轻松啊,为什么要有线程池这个东西呢?

虽然线程是个轻量级的东西, 但是对于互联网应用来说,如果每个用户的请求都创建一个线程,那会非常得多,服务器也是难于承受, 再说了,众多的线程去竞争CPU,不断切换,也会让CPU调度不堪重负,很多线程将不得不等待。所以前辈们的思路就是(1)用少量的线程 (2) 让线程保持忙碌

640?wx_fmt=png

640?wx_fmt=png

奥,就是说只创建一定数量的线程,让这些线程去处理所有的任务,任务执行完了以后,线程并不结束,而是回到线程池中去,等待接受下一个任务。

640?wx_fmt=png

这些线程可以预先创建,任务来了就不用临时再创建了,立刻开始服务。

640?wx_fmt=png

640?wx_fmt=png

预先创建? 您刚才不是说线程是程序代码的执行吗? 它是个动态的东西,怎么可能预先创建? 如果真的创建起来了,就会调用run方法, 马上执行完了, 线程就结束了!

你忘了重要的一点,线程的状态。 当线程池的线程刚创建时,让他们进入阻塞状态:等待某个任务的到来。 如果任务来了,那就好办,唤醒其中一个线程,让它拿到任务去执行即可。

640?wx_fmt=png

640?wx_fmt=png

可是怎么让他们进入阻塞状态?

看来之前的图我白画了, BlockingQueue听说过没有? 没听说过? 其实很简单,就是一个线程调用它的take()方法取数据时, 如果这个Queue中没有数据,该线程会阻塞;同样,一个线程调用它的put方法放数据时,如果Queue满了, 也会阻塞。

640?wx_fmt=png

640?wx_fmt=png

640?wx_fmt=png

奥,看来线程池中每个线程的run()方法中,要设置一个循环,每次都尝试从BlockingQueue中获取任务,如果Queue是空的,就阻塞等待, 如果有任务来了,就会通知到线程池的某一个线程去处理,处理完了以后,依然试图从BlockingQueue中获取任务,就这么依次循环下去。

线程池中的Worker线程:
public class WorkerThread extends Thread {

    private BlockingQueue<Task> taskQueue = null;
    private boolean       isStopped = false;
    //持有一个BlockingQueue的实例
    public WorkerThread(BlockingQueue<Task> queue){
        taskQueue = queue;
    }

    public void run(){
        while(!isStopped()){
            try{
                Task task = taskQueue.take();
                task.execute();
            } catch(Exception e){
                //log or otherwise report exception,
                //but keep pool thread alive.
            }
        }
    }
    ......略......
}

没错,你这个代码是一种简单的实现,我的老朋友Doug Lea大师写了一套非常好的实现,已经被吸收进JDK了,作为java.util.concurrent包的一部分,你直接调用即可,不用自己动手了。

640?wx_fmt=png

ExecutorService executorService = Executors.newFixedThreadPool(10);

executorService.execute(new Runnable() {
    public void run() {
        System.out.println("Asynchronous task");
    }
});

executorService.shutdown();

(完)

你看到的只是冰山一角, 更多精彩文章,请移步《2016文章精华》或者《2017文章精华》


码农翻身

用故事讲述技术

640

simulink调用C代码函数-C Caller模块 simulink中如何调用写好的C代码函数?使用C Caller模块调用C代码。 阅读详情

相关推荐

大模型学习路线之多模态模型

​ (Frankly speaking,博主太懒了)章节一只对CLIP、BLIP、BLIP2、InstructBLIP进行了整理,章节二以CLIP、VisualGLM作为实践案例。​ 实际上多模态模型除了CLIP、BLIP、BLIP2、InstructBLIP,还有LLaVA、miniGPT4,以及国内清华的VisualGLM、阿里的Qwen-VL等。(更多多模态原理可以看文末的参考说明)一、多模态模型 & 理论。

2401_86435672的博客 2293

Java获取另一个线程计算结果

【代码】Java获取另一个线程计算结果。

qq_44705614的博客 198

手把手教你用IN-Sight智能相机实现双向TCP/IP通讯(附网络调试助手推荐)

本文详细解析了IN-Sight智能相机实现双向TCP/IP通讯的完整流程。通过将相机同时配置为TCP服务器和客户端,构建了与上位机(如PLC、MES)稳定可靠的数据交换通道。文章深入剖析了发送(TCPDevice)与接收(ReadDevice)通道的独立配置、关键参数设置(如结束符CR+LF)、高效调试工具选择以及实战避坑指南,帮助工程师快速掌握这一构建柔性产线数据节点的核心技术。

qsc9012345的博客 671

java 线程池

线程池 线程相关类、集合 16、线程池 Why? 系统启动一个新线程的成本比较高,因为涉及到与操作系统交互。这个时候用线程池可以很好的提高性能, 尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池。 原理:(流程)线程池和数据库连接池有点类似的是,线程池在系统启动时创建大量空闲线程,程序将一个Runnable对象传给线程池, 线程池就会启动一条线程来执行该线程...

iteye_3191的博客 100

实战解析:利用Wireshark解密QQ通信协议

本文提供了一份利用Wireshark工具分析QQ通信协议的实战指南。文章详细介绍了从环境准备、捕获数据包到解析协议层的完整流程,重点演示了如何识别QQ号码字段、观察加密数据特征以及区分不同通信类型(如文本、文件传输)。通过此实践,读者可以深入理解网络分层模型和现代应用加密通信机制,提升网络协议分析与安全诊断能力。

weixin_29210727的博客 411

机器学习-深嵌交叉验证(Nested Cross-Validation)

Section I: Code and Analyses 第一部分:代码 from sklearn import datasets from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.svm import SVC from...

Santorinisu Blogs 2717

Java线程任务 创建一个单独的任务线程并提交到线程池中执行 案例

awaitTermination() 作用即: 阻塞,直到所有任务在 shutdown 请求后完成执行,或发生超时,或当前线程中断,以先发生的为准。在未来的某个时间执行给定的命令或任务,可能在一个新的线程中执行,也可能在一个线程池中执行,或在调用的线程中执行,这由 Executor 的实现决定。此时结果依旧一致 但是 main 方法没有停止运行,依旧处于运行中,添加 shutdown() 后其实任务已经停止。可见 awaitTermination() 作用与JDK 接口描述的一致 其实并不是必须的。

weixin_44131922的博客 1964

【深度学习】通俗易懂讲解循环神经网络LSTM

目录 1、循环神经网络(Recurrent Neural Networks) 2、长依赖存在的问题 3、LSTM 网络 4、LSTMs的核心思想 5、一步一步理解LSTM 6、LSTM的变种 7、总结 1、循环神经网络(Recurrent Neural Networks) 人对一个问题的思考不会完全从头开始。比如你在阅读本片文章的时,你会根据之前理解过的信息来理解下面看到的文字。...

Nieson2012的专栏 2733

线程与进程,你真得理解了吗

相信大家面试时一定没少被一个问题刁难,那就是进程和线程的区别是什么?这个问题延申开来并不像表面那么简单,今天就来深入一探。 开始前先看一组非常传神的图例,相信可以帮助你更好理解进程与线程的概念: 1 进程与线程的关系和区别 什么是进程 进程可以说是一个“执行中的程序”。程序是指令、数据及其组织形式的描述,是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程。 进程有哪些特征? 进程依赖于程序运行而存在,进程是动态的,程序是静态的; 进程是操作系统进行

云深不知处 14万+

科研生产力革命:手把手教你打造基于大模型的Research Agent(文献调研+论文写作+思路激发)

本文介绍了如何利用Research Agent(科研智能体)提升科研效率。Research Agent能自主完成文献检索、信息整合和报告生成全流程,省去50%以上的基础工作时间。文章详细讲解了搭建运行环境的方法,包括工具准备和MetaGPT配置,并提供了两种实战方案:基础版Search-O1和专业版MetaGPT Research Agent。此外,还介绍了如何为垂直领域定制Agent,以及优化检索效果、改善报告逻辑等技巧。最后列举了学术研究、行业分析、内容创作和个人学习四大应用场景,帮助用户将AI从聊天工

LXY_LOVE36的博客 267

(王道408考研操作系统)第一章计算机系统概述-第三节2:中断和异常

注意!!! 本节内容和《计算机组成原理》中的中断有重合,如果需要详细了解的话可以点击链接跳转:(计算机组成原理)第七章输入和输出系统-第四节2:I/O方式之程序中断方式 为了内容完整,部分内容会借用上述文章 文章目录一:中断和异常的基本概念(1)中断和异常的定义 一:中断和异常的基本概念 在操作系统引入核心态和用户态这两种工作状态后,就需要考虑两种状态之间是如何切换的。 操作系统内核工作在核心态,用户程序工作在用户态,系统不允许用户程序使用核心态的功能,但是用户程序为了完成某些操作又必须使用这些功能

快乐江湖的博客 3535

科普文:Java基础系列之【线程和线程池伪共享:ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal小结】

‌ThreadLocal‌:单线程数据隔离(如 Spring 的 RequestContextHolder)。 ‌InheritableThreadLocal‌:简单父子线程数据传递(如单次任务拆分)。 ‌TransmittableThreadLocal‌:线程池、异步任务、分布式链路跟踪(如日志 traceId 透传) ‌类别‌ ‌优点‌ ‌缺点‌ ‌ThreadLocal‌ 轻量级、线程隔离性强、无锁性能高 父子线程无法传递数据、线程池场景失效、内存泄漏风险需手动清理 ‌InheritableThrea

为无为,事无事,味无味。 1459

操作系统实验四 进程运行轨迹的跟踪与统计(哈工大李治军)

实验4 进程运行轨迹的跟踪与统计 实验目的 掌握 Linux 下的多进程编程技术; 通过对进程运行轨迹的跟踪来形象化进程的概念; 在进程运行轨迹跟踪的基础上进行相应的数据统计,从而能对进程调度算法进行实际的量化评价,更进一步加深对调度和调度算法的理解,获得能在实际操作系统上对调度算法进行实验数据对比的直接经验。 实验内容 进程从创建(Linux 下调用 fork())到结束的整个过程就是进程的生命期,进程在其生命期中的运行轨迹实际上就表现为进程状态的多次切换,如进程创建以后会成为就绪态;当该进程被调度以

leoabcd12的博客 4654

科普文:Java基础系列之线程池最佳实践和踩坑处理

比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。如果我们设置的线程池数量太小的话,如果同一时间有大量任务/请求需要处理,可能会导致大量的请求/任务在任务队列中排队等待执行,甚至会出现任务队列满了之后任务/请求无法处理的情况,或者大量任务堆积在任务队列导致 OOM。一般建议是不同的业务使用不同的线程池,配置线程池的时候根据当前业务的情况对当前线程池进行配置,因为不同的业务的并发以及对资源的使用情况都不同,重心优化系统性能瓶颈相关的业务。

为无为,事无事,味无味。 968

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

科普文:Java基础系列之【线程池中的踩坑和填坑】

线程池是Java多线程编程中的强大工具,但只有正确使用,才能发挥其最大效能。希望今天的内容能帮助你在使用多线程的过程中少走弯路,加深认知。

为无为,事无事,味无味。 793

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

科普文:微服务之SpringBoot性能优化器动态线程池【Dynamic-Tp】特性和源码解读

dynamic-tp设计巧妙,代码中设计模式先行,结构清晰易懂,代码规整,同时提供了很多扩展点,通过利用了Spring的扩展,和JUC原生线程池优势,功能强大。

为无为,事无事,味无味。 2378

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

科普文:线程池、连接池、动态线程池

线程池是一种用于管理和重用线程的机制,它允许开发人员有效地执行并发任务。线程池能够有效地管理系统资源,通过限制并发任务的数量和重用线程,减少了线程创建和销毁的开销,提高了系统资源利用率。通过合理地配置线程池大小和任务队列,可以优化任务执行流程,降低了线程的上下文切换成本,提高了任务的执行效率和系统的吞吐量。线程池可以控制并发任务的数量,防止系统因创建过多线程而导致资源耗尽,从而提高了系统的稳定性和可靠性。

为无为,事无事,味无味。 1667

【多线程】线程池

介绍 线程总数一定 线程复用 为什么要用 使用场景 服务器面对多请求。(超过5个以上的请求,用线程池) 单线程-代码 EveryTaskOneThread.class /** * 单线程 */ public class EveryTaskOneThread { public static void main(String[] args) { Thread thread = new Thread(new Task()); thread.start(); }

瑞新の博客:bennyrhys 1066

科普文:Java基础系列之【线程池参数调优与技巧】

Java 中的线程池实现在Executor:线程池顶层接口,定义了线程池的 execute()方法,提交任务到线程池去执行。:线程池的具体实现接口,通过 submit()方法向线程池提交任务,返回 Future,可以通过 Future 获得任务的执行情况。:实现类似于 Timer 的计划任务调度功能。:是 ExecutorService 的一个实现类,也是 Java 中的线程池实现的核心实现类。通常情况下,我们是使用 ThreadPoolExecutor 来实现线程池功能。

为无为,事无事,味无味。 1297

科普文: Java web应用性能分析之【java线程池中线程共享数据总结】

常见的并发问题 竞态条件(Race Condition):多个线程同时访问和修改同一数据,导致结果依赖于线程的执行顺序。 内存一致性错误(Memory Consistency Errors):一个线程写入的值可能在另一个线程中看不到,除非使用适当的同步机制。 死锁:两个或多个线程相互等待对方释放资源,导致程序无法继续执行。 java线程池中线程共享数据分类 在分析并发问题前,我们有必要对java线程池中线程共享数据做一个分类,以便于更好的理解线程池和并发问题。 下面从 ‌“共享方式”‌(如显式同步

为无为,事无事,味无味。 984

科普文:Java基础系列之【线程池Executors类的9种创建线程池的方法及应用场景分析】

在Java中,Executors 类提供了多种静态工厂方法来创建不同类型的线程池。: 创建一个可缓存的线程池,如果线程池中的线程超过60秒没有被使用,它们将被终止并从缓存中移除。: 创建一个固定大小的线程池,其中 nThreads 指定了线程池中线程的数量。: 创建一个单线程的执行器,它创建单个工作线程来执行任务。: 创建一个固定大小的线程池,它可以根据需要创建新线程,但会按照固定延迟执行具有给定初始延迟的任务。: 创建一个工作窃取线程池,它使用多个队列,每个线程都从自己的队列中窃取任务。

为无为,事无事,味无味。 948

你知道ThreadPoolExecutor是怎么存储线程池状态和线程数量的么?

前言 最近在看ThreadPoolExecutor的源码,里面在处理存储线程池的状态和线程池里面的大小感觉特比有意思,所以单独拿出来和大家分享下~ 怎么去存储状态和工作线程数,我们一步步的来看看,最后最下总结,总结下为什么这么去做 分析 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); private static final int COUNT_BITS = Integer.SIZE - 3;//

zxlp520的博客 1283

实现一个简易的线程池——线程复用

实现一个简易的线程池一、行文介绍二、线程池的概念三、线程池的原理四、阻塞队列介绍五、概念科普六、简单的线程池模型实现七、分析总结 一、行文介绍 实际上本人没有实战地使用过线程池,写这篇文章仅仅是出于对线程复用的兴趣。 本文将通过在网上搜集到的资料以及自己编写的简易版的线程池来说明线程复用技术。 二、线程池的概念 线程池是一种提高系统性能的技术,可以创建一定数量的工作线程,当向线程池提交任务时,线程池中的空闲线程将会接收任务并执行,执行完之后线程并不会马上关闭,而是处于空闲状态,等待下一个任务的到达。 在这里

weixin_44308662的博客 433

java并发编程之线程,线程池,ansync线程池源码解析

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34356138的博客 226

科普文:搞懂java多线程

用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现。说这个话其实只有一半对,因为反应“多角色”的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的“生产者,消费者模型”。 很多人都对其中的一些概念不够明确,如同步、并发等等,让我们先建立一个数据字典,以免产生误会。 多线程:指的是这个程序(一个进程)运行时产生了不止一个线程 并行与并发: 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时

为无为,事无事,味无味。 1245

科普文:Java基础系列之Java线程池实现原理及其在美团业务中的实践【作者致远 陆晨】

以下文章来源于美团技术团队 ,作者致远 陆晨随着计算机行业的飞速发展,摩尔定律逐渐失效,多核CPU成为主流。使用多线程并行计算逐渐成为开发人员提升服务器性能的基本武器。J.U.C提供的线程池ThreadPoolExecutor类,帮助开发人员管理线程并方便地执行并行任务。了解并合理使用线程池,是一个开发人员必修的基本功。本文开篇简述线程池概念和用途,接着结合线程池的源码,帮助读者领略线程池的设计思路,最后回归实践,通过案例讲述使用线程池遇到的问题,并给出了一种动态化线程池解决方案。一、写在前面1.1 线程池

为无为,事无事,味无味。 1009
上一篇: 编程15年,如何才能成不了高手?
下一篇: 千呼万唤始出来
码农翻身
码农翻身 《码农翻身》作者 《码农翻身》作者
博客等级 码龄8年 3万+粉丝 1084原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值