一、并发编程基础概念:并行与并发、同步与异步、阻塞与非阻塞

好,咱们开始第一章。说实话,每次带新人时,我总会先问一个问题:你知道并发和并行到底有什么区别吗?很多人答不上来,或者觉得是一回事。嗯,这其实很要命。如果连底层概念都模糊,后面写SDK的连接池、线程池,迟早会踩坑。

我自己刚入行那会,也犯过这种错。记得有一次做高并发推送服务,我上来就开了100个线程,以为这就是「并行」。结果服务器CPU直接打满,吞吐量反而不如8个线程。后来才明白——我用的那台机器只有4核,操作系统只是在「并发」地切换线程,根本不是真并行。

所以这一章,我们先把地基打牢。我会用最直白的方式,把三组核心概念讲清楚:

  • 并行 vs 并发 —— 到底能不能同时干活?
  • 同步 vs 异步 —— 等还是不等,谁来等?
  • 阻塞 vs 非阻塞 —— 卡住了怎么办?

你可能会觉得这太基础,但相信我——SDK连接池调优、限流策略、超时重试,所有高阶玩法都根植在这些概念上。咱们别着急,慢慢来。


1.1 并行与并发:从「伪同时」到「真同时」

首先,什么是并发?说白了,就是两个任务看上去在同时进行。但底层呢?CPU可能只有一颗核心,它只是在任务间快速切换——就像你一边敲代码一边回微信,其实手只做一件事,只是切换得足够快。你感觉是「同时」,实际上叫「并发」。

那什么是并行?真同时——CPU有多个核心,每个核心各自跑一个任务,互不干扰。相当于你左手画圆、右手画方,两只手真的在同时动。

我给个最直观的比喻:

  • 并发:一条单行道,两辆车交替通过路口(你感觉车流没停过)。
  • 并行:两条车道,两辆车同时通过路口(物理上同时)。

这在SDK开发中有什么影响?举个例子,你写一个HTTP连接池。如果目标服务器只有一台(单点),你开再多线程也是并发——因为服务端同一时刻只能处理一个请求(假设单线程模型)。这时候你堆线程反而加剧上下文切换,不如用异步非阻塞。

⚠️ 我曾经犯过的错: 在2核4线程的虚拟机里,硬是启动了32个线程去拉数据。结果每个线程都在等待锁和IO,CPU 95%花在了切换线程上,吞吐量不到理论值的10%。所以后来我有个习惯:启动SDK前先看cpu_count(),然后乘以一个系数(通常1.5~2),而不是拍脑袋设线程数。

1.2 同步与异步:谁在等谁?

好,第二个概念:同步、异步。很多人跟阻塞非阻塞混为一谈。我来拆开讲。

同步:你调一个函数,必须等它执行完才能做下一件事。就像你发一条消息,对方必须回你,你才继续聊天——你不怕等,但你不能做别的。

异步:你调一个函数,不等它完成,立即返回。你继续干你的活,等函数有结果了,通过回调、事件、Future等方式通知你。就像你发了一个邮件,然后就去写代码了,等对方回复时你再处理。

在SDK开发中,同步模型简单好理解,但容易让调用方阻塞。比如:

// 同步调用:阻塞等待连接建立
Connection conn = pool.getConnection();
// 只有拿到conn才能往下走

而异步模型则允许调用方在等待期间做其他事:

// 异步调用:立即返回Future
Future<Connection> future = pool.getConnectionAsync();
// 这里可以干别的,比如解析请求
doOtherWork();
// 等需要连接时,再get
Connection conn = future.get();  // 可能会阻塞,但延迟了等待

你想想看,这两种设计对SDK的吞吐量影响有多大。我在做一个消息队列SDK时,早期采用同步阻塞方式,客户端并发一高就卡死。后来改成异步回调+事件循环,同样的硬件配置,吞吐量翻了5倍。所以现在主流SDK(像Netty、gRPC)都走异步路线。


1.3 阻塞与非阻塞:卡住 vs 立刻回来

这个最容易混淆。我直接说人话:

  • 阻塞:调用被挂起,直到条件满足才返回。CPU切换到别的线程。
  • 非阻塞:调用立即返回,要么有结果,要么通知你「没准备好,你一会再来问问」。

举个例子,你去银行柜台办事。阻塞模式就是:你拿号,坐着等,啥也不干,直到轮到你。非阻塞模式:你拿完号,出去逛一圈,隔几分钟回来看看叫号到哪了——每次回来问“轮到我了吗?”柜员说“没呢,过会儿再来”,然后你继续逛。

在SDK连接池里,这两种模式很常见:

模式行为典型API
阻塞 调用线程挂起,等待连接就绪 getConnection()(默认阻塞)
非阻塞 立即返回,未就绪则返回错误或标志 tryAcquire() 返回 null 或立即抛异常
非阻塞+轮询 立即返回,用户循环询问状态 poll() + 忙等待
💡 我的经验: 在SDK内部,我通常会把阻塞连接池做成可配置的。对于低延迟场景,应该用非阻塞+有限次重试;对于批处理场景,阻塞更简单。不要一刀切,要根据调用方的容忍度来选择。

1.4 概念全景图:一张图看懂三组关系

说了这么多,我画一张图帮你理清脉络。这张图把并行/并发、同步/异步、阻塞/非阻塞放在一个框架里,你看看它们是怎么关联的。

并发编程基础概念全景图 并发编程模型 并行 vs 并发 同步 vs 异步 阻塞 vs 非阻塞 并发(伪同时) 并行(真同时) 同步(等结果) 异步(不等) 阻塞(卡住) 非阻塞(立即回) 💡 实际开发中如何选择? 高吞吐SDK → 异步非阻塞;低延迟SDK → 同步非阻塞;批处理SDK → 同步阻塞

从图上你能看出来,这三对概念其实是正交的。一个系统可以同时是「并发+同步+阻塞」,也可以是「并发+异步+非阻塞」。SDK连接池优化,就是要在这些维度里找到最佳组合。

⏺️ 一句话总结:
并行依赖硬件,并发是软件玩法;同步让你等,异步让你玩;阻塞让你挂起,非阻塞让你轮询。别搞混,接下来每一章都会用到这些底牌。