像面试一分钟自我介绍一样的知识
“进程和线程有什么区别?”
还在求职的时候,这个问题总是稳稳地排在“面试高频问题 TOP 10”里。我像机器一样回答:
“进程是正在运行的程序,线程是在进程内部执行的流程单位。进程之间不共享资源,而线程之间共享资源。”
面试官点了点头,我也误以为自己已经完全理解了这个概念。但直到在工作中撞上“并发问题(Concurrency Issue)”之前,我完全不知道这句话背后藏着多么可怕的东西。
“浏览量明明应该涨 100,为什么只涨了 98?”“为什么一挂上 Excel 下载,网站就卡住了?”
我的代码自己一个人跑的时候完美无缺,可一旦多个用户同时涌进来,就乱成了一锅粥。我原以为只要多加几个“工人”就能解决,却不知道工人越多,管理他们的成本(Context Switching)就会呈指数级增长。

数字物流中心里的工厂和工人
让我们回到“数字物流中心”这个世界观。在计算机里运行一个程序,就相当于在物流中心里开设一座“工厂(Factory)”。
1. 进程(Process):独立的工厂
- 定义:从操作系统(厂长)那里分配到资源的“独立工作单位”。
- 特点:各自都有独立的厂房。就算隔壁工厂倒塌了(出错),我的工厂也安然无恙。
- 成本:要建一座工厂,既需要土地(内存),也得办理营业登记(PID)。“又贵又重。”
- 共享:原则上不能拿隔壁工厂的东西。(得用一种叫 IPC 的复杂通信方式)
2. 线程(Thread):工厂里的工人们
- 定义:在进程(工厂)里实际干活的“工人”。
- 特点:一座工厂里可以有好几个工人(多线程)。
- 成本:雇一个新工人,比新建一座工厂要“便宜、轻巧得多”。
- 共享:这才是关键。工人们共用“食堂(Heap)和休息室(Data)”。

[Code Verification] 共享的悲剧(并发问题)
“线程共享资源。”这句话在面试现场听起来像是优点,但在实际工作中,却可能成为“灾难”的种子。
还记得我们上次学过的“堆(Heap)”吗?线程们共享这块堆区域。也就是说,线程 A 正在处理的数据,线程 B 可以跑过来直接覆盖掉。
这就叫“竞态条件(Race Condition)”。我们用代码来确认一下。
public class RaceConditionTest {
static int count = 0; // 存放在堆(Heap)中的共享变量
public static void main(String[] args) throws InterruptedException {
// 雇用两名工人(线程)
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) count++;
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) count++;
});
t1.start(); // 开工!
t2.start(); // 开工!
t1.join(); // 等到下班为止
t2.join(); // 等到下班为止
System.out.println("最终结果: " + count);
}
}
预期结果:两个人各加了 10,000 次,所以应该得到 20,000。
实际结果:15,482、18,931……每次运行结果都不一样,而且就是到不了 20,000。
原因:
- 线程 A 从堆中读取
count(100),拿到栈里。 - 就在这时,线程 B 也从堆中把
count(100)读走了。 - A 把它变成 101,存回堆里。
- B 也(因为自己读到的是 100)把它变成 101,存回堆里。
- 明明加了两次,结果却等于只加了一次。
这就是实际工作中那种“数据偶尔会被吞掉”的 bug 的真面目。工人们彼此不打招呼,同时去动同一本账本,于是酿成了这场悲剧。
如果用数据库(DB)来打比方,这就和“在没有事务(Transaction)的情况下,多条查询同时打过来”一模一样。在没有保护装置(Lock)的情况下同时修改账户余额,就会发生钱凭空蒸发的可怕事故。就像我们在 DB 中用 ROLLBACK 或 COMMIT 来保护数据一样,在代码层面也必须要有 Synchronized 这样的锁机制。
实际工作中的取舍(Trade-off):多进程 vs 多线程
那么在实际工作中,什么时候该用哪一种呢?
1. Chrome 浏览器的选择:多进程
早年的 Internet Explorer,只要一个标签页卡死,整个浏览器就会关掉(多线程方式)。而 Chrome 会把每一个标签页都作为“独立的进程(工厂)”来启动。
- 优点:即使一个标签页卡死了(工厂爆炸了),其他标签页(隔壁工厂)也安然无恙。(稳定性)
- 缺点:极其吃内存。这就是 Chrome 成了“内存杀手”的原因。(成本)
2. Web 服务器(Spring、Node.js 等)的选择:多线程
服务器要处理数千人的请求。如果每来一个请求就建一座工厂(进程),服务器会直接崩掉。所以会在一个进程里安排大量线程(工人)来处理。
- 优点:占用资源少,速度快。(效率)
- 缺点:只要有一个线程出错、把进程搞崩,整个服务器就会挂掉。而且刚才看到的并发问题,必须由开发者自己去防范(Synchronized)。

结语:施展分身术,就得承担责任
今天我们一起了解了工人们的工作方式——“进程和线程”。
- 进程:安全但昂贵的独立工厂。
- 线程:轻巧快速、但有冲突风险的共同作业者。
现在你应该明白了,“并发问题”这个词并不只是面试用语。无数线程工人正马不停蹄地进出名为堆(Heap)的共享仓库。在这片混乱中建立秩序,正是后端开发者的真本事。
不过等一下,工人一多,该怎么决定谁的活先干呢?厂长(OS)又是如何管理几百名工人的?下一次,我们来聊聊厂长最头疼的工作——“调度与上下文切换(Context Switching)”。


