IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

标签:多线程

共 24 篇相关文章

IT 累计浏览 2,281

对Android中的多图片异步加载的重新思考

这篇讲的是图片加载策略引发的性能反思。作者从开源中国客户端的一个实际问题出发:早期版本采用多线程并发下载图片,结果在列表加载时,图片反而出现明显的延迟和卡顿,尤其是在网络条件不佳的情况下。 核心发现直觉与效果的悖论:为什么传统观念中能提升资源利用率的并发,在这里却拖了后腿?文章用了一个生动的比喻——如同一个人同时推进两个相似项目,虽然总工时可能缩短,但单个项目完成的周期却被拉长,导致用户感知到的响应变慢了。 作者进一步剖析了 Android 系统本身的演进:在 Android 3.0 之后,AsyncTask 从默认的并发执行改为了串行执行队列。这个看似“退步”的设计,实际上是为了规避高并发下可能引发的资源耗尽与崩溃风险。在图片加载场景中,配合滚动时及时取消无效任务的机制,串行执行不仅更安全,从用户体感上看也避免了任务间的相互“干扰”,使图片能更连贯、流畅地逐张呈现。 这篇技术复盘最终指向一个启发:在移动端资源受限的环境下,技术方案的选择需要超越纸面的性能理论,紧密结合具体的使用场景和用户体验来重新审视。有时,看似“更慢”的串行,反而是通往“更快”感知的路径。

IT 累计浏览 2,951

菜鸟不要怕,看一眼,你就会用GCD,带你装逼带你飞

这篇讲的是iOS开发中GCD多线程技术的入门实战指南,作者没有堆砌理论,而是从最常用的队列概念切入,手把手教初学者正确使用。文章核心对比了串行队列与并发队列:前者任务按顺序执行,后者则能同时处理多个任务互不阻塞——通过简单的代码和清晰的线程日志输出,直观展示了两者的关键差异。接着介绍了系统预置的Global Queue(并发)与Main Queue(串行主线程)的协作模式,这是日常开发中最常见的“异步操作后回调主线程”范式。此外,作者还详细演示了如何通过dispatch_set_target_queue为自定义队列设置优先级,以及用dispatch_after实现主线程延迟操作,并解释了这些方法与RunLoop的关系。全文通过可运行的代码片段和真实的输出结果,让读者能迅速理解GCD各组件的工作机制与适用场景。

IT 累计浏览 3,380

[Java基础教程]第十三章-Java多线程

这篇讲的是Java多线程,作者从“如何让服务器同时处理多个客户端”这个实际问题出发,解释了并发编程的必要性。文章用一个生动的买票比喻,把“串行”、“并行”和“并发”这三个容易混淆的概念讲得明明白白:排队是串行,多窗口同时卖票是并行,而一个售票员同时应付多个询问者则是并发。 更深入一点,作者还探讨了为什么在多核CPU普及的今天,讨论并发时常常仍基于单核模型,点明了任务调度、内存访问等现实约束。核心部分是用Java的Thread类重构服务器代码,展示了如何为每个新连接创建独立线程来处理。文章不仅给出了基础实现,还进一步模拟了10个用户同时连接的场景,并加入了一个将聊天记录缓存后批量写入文件的ChatLogManager类,让示例更贴近生产环境中的考量。 整体而言,这篇教程从概念辨析到代码实践,一步步带着读者理解并发的本质,并快速掌握Java多线程编程的入门要点。

IT 累计浏览 3,211

【Java并发编程实战】—– AQS(一):简介

这篇讲的是Java并发编程中作为JUC同步器基石的AbstractQueuedSynchronizer(AQS)框架的核心原理。作者从ReentrantLock、CountDownLatch等同步器获取锁的不同方法出发,引出对统一框架的需求,从而介绍了AQS的诞生背景。 文章着重解析了AQS实现的三大核心机制。首先,它用一个volatile的int型state变量来表示同步状态,通过CAS操作进行原子更新,这构成了锁获取与释放的状态基础。其次,AQS内部维护了一个变种的CLH双向队列,用于管理等待线程。这个设计的巧妙之处在于,它摒弃了原始CLH的自旋,转而让节点在队列中通过状态位安全地阻塞与唤醒,并能高效处理超时和取消操作。最后,AQS通过队列中Node的SHARED和EXCLUSIVE标记,统一支持了共享锁与互斥锁两种模式。 在阻塞唤醒机制上,AQS没有沿用Java内置锁的wait/notify,而是采用了更底层的LockSupport.park()与unpark()本地方法。整篇文章清晰地勾勒出AQS的骨架,为后续深入分析各类同步器的具体实现打下了扎实的基础。

IT 累计浏览 4,791

C++线程池实现原理

这篇讲的是C++线程池中一个核心但常被忽略的机制——闭包(Closure)是如何工作的。作者没有一开始就堆砌复杂概念,而是从一段注释丰富的简单示例代码切入,展示了使用线程池的标准写法。 然后,他带读者聚焦到那个让人困惑的`NewClosure`函数。文章的巧妙之处在于,它揭示了这个“闭包”其实是一个简单的模板对象。其核心思路是:通过模板类`ObjClosure1`,将对象指针、成员函数指针以及参数值,这三样东西打包存储起来。当线程池的工作线程取出这个任务时,调用的`Run()`方法内部,再通过`(p_->*fun_)(arg1_)`这样的C++成员函数指针调用语法,把这些元素重新组合起来执行。 文章把这个过程比作“打包”与“拆解执行”,解释得清晰直白。作者的目标是让初学者看完后,从“不明觉厉”变为“原来如此”。对于想了解线程池任务封装原理,却又对模板和函数指针感到头疼的C++开发者来说,这是一篇很好的入门拆解。

IT 累计浏览 7,584

并发编程系列之一:锁的意义

这篇讲的是并发编程中“锁”的根本意义,远不止让一段代码串行执行那么简单。作者从一个经典的多线程计数问题出发:500个线程对一个全局变量执行++操作,若无任何保护,最终结果几乎总是小于预期的5000000,原因是++操作本身不是原子的,发生了数据竞争。 接着,文章展示了如何用自旋锁(Spinlock)保护这段代码,并成功保证了结果的正确性。但这仅仅是表象。作者深入剖析,提出了一个关键问题:在编译器和CPU都可能对指令进行乱序重排的情况下,如何确保锁保护区域内的代码不会被“甩”到锁外并发执行? 这便引出了锁更深层的两重意义:第一重是字面上的互斥,保证同一时刻只有一个线程进入临界区。第二重,也是更关键的一重,是内存操作的可见性与顺序性——即锁的“获取(Acquire)”和“释放(Release)”语义。它们构成了强大的内存屏障,防止了前后的读写操作被错误重排,从而确保了被保护的代码及其对共享数据的修改,对其他线程表现出确定且正确的顺序。理解这一点,才算真正读懂了锁在并发世界中的核心价值。

IT 累计浏览 2,621

聊聊多线程程序的load balance

这篇讲的是,如何在一个常见的“接收者-工作线程池”模型中,主动优化负载均衡以提升性能。作者从一个大家熟悉的设计出发:一个 receiver 线程接收请求,放入队列,通过条件变量唤醒任意一个 worker 处理。他敏锐地指出,完全依赖内核的调度和负载均衡可能带来问题。 核心问题有两个:如果 worker 线程远多于 CPU 核心数,唤醒时几乎无法均匀分配到不同 CPU,导致某些核过载而某些核空闲,形成“伪并发”。其次,即使 worker 数量合理,内核的负载均衡也未必能确保将任务分配到不同的物理核心(避免争抢共享缓存和计算资源)。 对此,作者提出了应用层的“微调”方案。一方面,将 worker 线程数控制在接近或略小于 CPU 核心数。另一方面,更关键的是,通过线程绑定(affinity)固定 worker 在特定 CPU 上,并设计一个分级的条件变量唤醒机制。这能确保新任务被优先分配给空闲或低负载物理核心上的 worker,从而主动实现更优的负载分布。 文章通过精心设计的实验验证了结论。例如,将 worker 线程数从 240 降至 24 后,CPU 利用率从 2200% 提升至接近 2400%。启用绑定线程和分级唤醒后,处理 12 个并发任务时性能得到进一步提升。作者也发现,对于依赖内存缓存的任务(如 mmap 读文件),让 worker 集中在相邻 CPU 上反而可能提升性能,这体现了负载均衡策略需具体场景具体分析。 作者通过细致的对比实验表明,这些在应用层主动进行的微调,有时能取得比等待内核调度更优的效果。

IT 累计浏览 3,162

Linux程序链接时-lpthread对程序正确性的影响

作者线上服务频繁卡死,CPU使用率飙升。通过pstack堆栈分析,发现线程大量阻塞在pthread_cond_signal和mutex解锁操作上,但本应轻量的unlock函数却异常消耗资源。排查指向了链接时未指定-lpthread的隐患。 文章深入剖析了这一常见陷阱背后的机制。在glibc中,pthread相关函数(如mutex、条件变量)存在“空实现”以优化单线程程序性能。只有当程序显式链接libpthread.so后,其正确的多线程实现才会覆盖libc中的符号。作者通过readelf和nm工具对比发现,动态库通过versioned symbol(如pthread_cond_signal@GLIBC_2.3.2)实现这一覆盖,而非静态库使用的weak symbol。 关键在于,若主程序或最终可执行文件未链接pthread库,即使动态库本身依赖多线程,程序启动时加载的仍是libc中的空实现,导致死锁等严重问题。作者通过复现实验证明,只要最终可执行文件链接了-pthread,即便中间动态库未链接,也能通过符号版本机制正确解析到pthread库的实现,从而规避风险。

IT 累计浏览 3,629

使用valgrind的callgrind工具进行多线程性能分析

性能分析常让人头疼,尤其在多线程程序里找出瓶颈更不容易。这篇讲的是如何用开源的Valgrind套件中的Callgrind工具,来完成多线程程序的性能剖析。作者从实际命令出发,演示了从数据采集到图形化分析的完整流程。 核心步骤很清晰:先用`valgrind --tool=callgrind`运行目标程序生成分析文件。如果是多线程程序,加上`--separate-threads=yes`参数,就能为每个线程单独生成一份数据,比如`callgrind.out.31113-01`、`-02`等,便于逐个排查。采集到的数据再通过`gprof2dot.py`脚本转换成dot格式,最后用`dot`命令生成PNG调用图。 最终得到的图形能直观展示函数的调用关系和耗时分布,让性能热点一目了然。文章没有空谈理论,而是给出了可直接复制的命令和参数,对需要快速定位代码性能问题的开发者来说,是个实用且上手快的方案。

IT 累计浏览 3,678

JAVA多线程面试题

这篇文章系统梳理了Java多线程领域最常被问及的25个核心问题,堪称一份精炼的面试准备与知识巩固指南。 内容覆盖了从基础概念到底层原理的完整链条。开篇便厘清了进程与线程的根本区别,指出线程作为“轻量级进程”如何共享资源以提升效率。随后深入剖析了线程的创建方式(实现Runnable接口与继承Thread类)、生命周期状态(从New到Dead的流转),以及用户线程与守护线程的关键差异。 文章不仅止于理论,更聚焦于实战与调优。它详细解释了线程间通信的底层机制——为什么wait()、notify()等方法定义在Object类中且必须在同步块内调用,并对比了同步方法与同步块的性能影响。对于并发编程中的痛点,如线程安全,文章列举了同步、volatile、原子类等多种保障手段。关于死锁的分析、线程池的创建、以及ThreadLocal的用途,也都给出了清晰的定义与实用的指导。最后,文章还涉及了线程转储(Thread Dump)的获取与分析,为解决复杂并发问题提供了工具。 整体而言,这篇文章没有泛泛而谈,而是将每一个“为什么”和“怎么做”都讲得扎实具体,非常适合Java开发者用来查漏补缺,快速构建起关于多线程面试与实践的知识框架。

IT 累计浏览 4,224

深入分析Volatile的实现原理

这篇技术分析从Java内存模型对Volatile的定义出发,深入到x86处理器架构层面,揭示了其保证共享变量可见性的硬件实现机制。 文章通过分析JIT生成的汇编代码,指出Volatile写操作会触发带有Lock前缀的指令。这条指令会引发两个关键动作:强制将当前处理器的缓存行写回系统内存,并使其他处理器中该地址的缓存失效。这本质上是利用了处理器的缓存一致性协议(如MESI)和“缓存锁定”机制,以确保修改的原子性和全局可见性。 更巧妙的是,文章介绍了Java并发大师Doug Lea在JDK7中利用Volatile变量进行性能优化的实战案例。在LinkedTransferQueue中,他通过将队列头尾节点填充至64字节(一个缓存行的宽度),避免了它们因被读入同一缓存行而在多核处理器下相互锁死,从而显著提升了高并发下的出入队效率。文章最后也客观指出,这种追加字节的优化并非万能,需结合处理器缓存行大小与变量访问频率来决策。

IT 累计浏览 2,256

[译文]关于移动Web性能的5个神话

Sencha的CEO Michael Mullany撰文回应了此前引发热议的“移动Web应用为何慢”一文,他指出该文数据虽基本正确,但解读存在偏差且忽略了更关键的图形性能。文章系统驳斥了五个关于移动Web性能的常见误解。 首先,移动Web性能瓶颈主要在于浏览器渲染优化、DOM操作和GPU加速,而非JavaScript本身。其次,过去四年超过50%的JavaScript性能提升源于软件优化,而非单纯依赖硬件升级。再者,移动浏览器性能远未停滞,不同浏览器在各自领域存在10倍以上的差距,优化空间巨大。同时,未来的硬件迭代将通过更快GPU、内存带宽和多线程并行化持续带来性能飞跃。最后,现代浏览器采用的增量垃圾收集机制已大幅改善停顿问题,垃圾回收不再是无法逾越的性能杀手。 作者结合iOS和Android设备长达四年的性能测试数据,展示了JavaScript与DOM操作性能的显著提升,这些进步远超摩尔定律预期。文章强调,优秀的开发者使用现代Web框架能够构建出体验流畅的移动应用,性能的持续进化让开发者对移动Web的未来充满信心。

IT 累计浏览 2,413

Ruby的多线程应用服务器介绍

这篇讲的是随着Rails 4.0默认开启多线程模式,Ruby Web开发迎来了多线程服务器选型的新阶段。作者对三款主流选择进行了对比分析。 Rainbows和Puma都支持master-worker集群模式,能充分利用多核服务器。Rainbows脱胎于稳定的unicorn,提供了丰富的进程控制信号,适合对稳定性和运维控制要求高的大型应用。Puma的特色在于线程数可根据请求量在设定范围内自动伸缩,且单进程模式更节省内存,对中小型应用更友好。而Zbatery是极简的单进程多线程版本,是三者中最省内存的,适合在VPS上托管多个低流量应用。 作者的性能测试显示两者差异不大,选择更多取决于场景:追求稳定与可管理性选Rainbows,希望节省资源与灵活伸缩则Puma更佳。

IT 累计浏览 2,588

有关读写锁

这篇文章讲的是为什么在并发编程中需要引入读写锁。作者从最基本的互斥锁(Mutex)出发,指出在“读多写少”的场景下,互斥锁会让所有线程串行化,即使多个线程只是读取数据,也无法并行,这严重限制了程序的吞吐量和性能。 核心方案就是读写锁(Read-Write Lock)。文章清楚地解释了它的关键差异:将锁分为“读锁”和“写锁”。多个线程可以同时持有读锁,进行并发读取,实现了真正的读并行;而写锁则是独占的,一旦有线程想写入数据,它必须等待所有读锁和其他写锁被释放。这种设计巧妙地在保证数据一致性(通过写锁的独占性)的同时,最大化地提升了读操作的并发性能。 文章进一步对比了它们的适用场景。互斥锁适合写操作频繁,或者读写耗时都差不多的简单场景。而读写锁则明确针对读远多于写,且读操作耗时较长的应用,例如缓存系统、配置中心或任何读多写少的共享数据结构。理解这一点,就能在性能瓶颈出现时,知道该从哪里优化锁策略。

IT 累计浏览 8,100

C++ 多线程编程总结

这篇讲的是C++开发者在追求高并发与实时性时,如何通过多线程编程提升程序效率的实战心得。作者从实际开发需求出发,没有空谈理论,而是直接总结了几类常见的多线程优化路径。 文章内容扎实,比如会具体聊到如何创建和管理线程以避免不必要的开销,对比不同同步原语(如互斥锁、条件变量)的适用场景与性能权衡,以及如何设计任务队列来平衡负载。这些经验点直指开发者日常编码中的真实痛点。 对于正在处理高并发服务、实时计算或对延迟敏感的系统性能瓶颈的工程师而言,这种将散落在各处的实践知识系统化的梳理很有价值。它能帮助你在方案设计阶段就考虑到关键的多线程因素,避免后续踩坑。

IT 累计浏览 6,586

从Java视角理解CPU上下文切换(Context Switch)

这篇从Java开发者的视角,探讨了CPU上下文切换对程序性能的直接影响。文章首先解释了操作系统如何通过时间片轮转实现多任务并发,而这一过程必然伴随着保存和恢复任务状态的开销,即上下文切换。这种切换不仅带来寄存器保存、调度器执行等直接消耗,还会因多核缓存共享等问题产生间接影响。 作者指出,在Java多线程编程中,线程因竞争锁或等待IO而频繁挂起,会显著加剧上下文切换,反而可能拖慢整体性能。为了量化这一开销,文章提供了一个简单的Java实验:两个工作线程互相唤醒与挂起,模拟高频率的上下文切换场景。实测数据显示,在特定硬件上,一次上下文切换平均耗时约11至13微秒。这导致看似简单的循环执行耗时数十秒,而vmstat命令也直观展示了系统上下文切换次数的激增。 通过这个实验,文章清晰地揭示了上下文切换的实时代价,帮助Java开发者理解为何盲目增加线程数不一定能提升吞吐量,甚至可能是性能瓶颈的来源。

IT 累计浏览 4,607

对protostuff和java序列化的小测试

这篇讲的是作者对Protostuff和Java原生序列化机制进行的一次性能小测。作者从一个常见的序列化需求出发,直接对比了这两种方案在序列化速度、生成的数据大小以及反序列化效率上的表现。 测试结果直观地展现了几项关键差异:Protostuff在序列化和反序列化速度上普遍更快,生成的数据体积也更小。这些优势主要源于其实现原理——它跳过了Java序列化必需的反射过程,采用了更紧凑的编码方式。文章同时也指出了Java序列化在跨语言兼容性和与JVM生态无缝集成方面的固有优点。 对于开发者来说,这个对比的启发很明确:如果项目环境统一为Java,且对性能或存储空间有较高要求,Protostuff是值得考虑的替代方案;而当需要跨语言通信或依赖JVM特定功能时,Java原生序列化仍是稳妥的选择。

IT 累计浏览 8,807

浅析C++多线程内存模型

这篇讲的是C++11标准中引入的一个关键底层特性:多线程内存模型。作者从多线程编程中一个常见的困惑出发——为什么我的同步措施失效了?——引出了这个问题的根源:在不同的编译器、CPU架构和优化级别下,指令的执行和观察顺序可能与你写的代码顺序不一致。 文章的核心是解释C++内存模型如何为多线程程序定义了一套统一的“因果律”和“可见性”规则。它清晰区分了几种不同的内存序,比如宽松的原子操作、具有同步关系的获取-释放序,以及最强的顺序一致性。通过对比,你能看出每种序提供了怎样的保证,又可能带来多大的性能开销。 理解这些规则,是编写既高效又正确的并发代码的基石。它决定了你该在何处加锁,何处使用原子变量,以及如何设置内存序参数,来避免那些最隐蔽、最难调试的数据竞争与诡异Bug。

IT 累计浏览 2,611

spinlock剖析与改进

这篇讲的是操作系统中常见的同步原语——spinlock(自旋锁)的深度剖析与实践优化。作者从标准自旋锁的实现原理出发,解释了其通过忙等待避免线程切换的设计初衷,但也直接点明了在特定场景下的性能瓶颈:当锁被持有时,其他等待线程会持续空转CPU,造成资源浪费。 文章的核心价值在于“改进”部分。作者详细拆解了标准实现的问题,并提出了具体的优化思路,比如结合 ticket spinlock 或 MCS lock 的机制来减少缓存行争用与不必要的CPU空转。通过对比分析,清晰地展示了不同实现在多核环境下对性能、公平性和扩展性的实际影响。 从淘宝子嘉的视角来看,这并非纯理论探讨,而是结合了生产环境经验。他不仅讲清楚了“是什么”和“为什么”,更给出了“怎么改”的实践方案,对于需要处理高并发锁竞争的开发者来说,提供了切实可行的优化方向。

IT 累计浏览 3,648

存储方式与介质对性能的影响

这篇文章聚焦于存储性能的核心影响因素:存储介质(HDD 与 SSD)和访问方式。作者开篇就抛出了一个基础但关键的问题:为什么同样的数据,存储在机械硬盘和固态硬盘上,体验会天差地别?文章没有停留在“SSD快,HDD慢”的结论上,而是深入到了物理原理和工作模式的层面。 核心差异在于,HDD依赖磁头在盘片上物理寻道,其随机读写性能受机械结构的制约;而SSD基于NAND闪存,通过电信号直接访问存储单元,天然擅长高并发的随机IO。文章也对比了不同的访问模式:顺序读写时,两者的差距主要体现在带宽上;而在面对数据库查询、虚拟机启动这类典型的随机读写场景时,SSD的低延迟优势会被彻底释放,性能差距可达数十甚至上百倍。 基于这些分析,文章最终指向一个实际的选型建议:对于需要高IOPS(每秒读写次数)和快速响应的应用,如线上交易系统、热数据存储,SSD是刚需;而对于大容量、访问频率较低的冷数据备份或归档场景,成本更低的HDD仍是高性价比之选。理解介质的特性与工作负载的匹配关系,才是进行存储架构设计的第一步。