Skip to content

进程与线程:计算机里的「房子」和「房间」

凌晨 2 点,你的程序突然卡住了。你打开任务管理器,发现内存占用 2GB——这是「进程」在呼吸。而旁边那个 CPU 占用 100% 的条目,是某个「线程」在疯狂运算。

进程和线程,是 Java 并发编程的地基。你可能每天都在用,但真的理解它们的区别吗?


进程:资源分配的「房子」

进程(Process) 是操作系统分配资源的基本单位。

想象进程是一栋独立的房子:

  • 每栋房子有自己独立的「房产证」(地址空间)
  • 房子里的家具(内存)只能自己用,别人进不来
  • 房子之间说话需要打电话(进程通信)
java
// Java 中,每个程序运行都对应一个进程
// 通过 Runtime 或 ProcessBuilder 可以创建新进程
ProcessBuilder pb = new ProcessBuilder("notepad.exe");
Process p = pb.start();

进程的核心特征

特征说明
独立性每个进程有独立的虚拟地址空间
资源拥有持有 CPU、内存、文件句柄、I/O 设备等资源
开销大创建、切换需要操作系统介入,用户态→内核态切换
通信复杂需要 IPC(管道、消息队列、Socket 等)

线程:CPU 调度的「房间」

线程(Thread) 是 CPU 调度的最小单位。

如果说进程是一栋房子,线程就是房子里的房间:

  • 所有房间共享房子的基础设施(厨房、卫生间)
  • 每个房间有自己独立的「床铺」(程序计数器)和「工作台」(栈)
  • 房间之间说话不用打电话,直接喊就行
java
// Java 中,线程是最基本的调度单位
Thread t = new Thread(() -> {
    // 这个代码运行在一个独立的线程中
    System.out.println("我是线程,我有自己的执行流");
});
t.start();

线程的核心特征

特征说明
共享资源共享进程的堆内存、文件句柄、静态变量
独立资源独立的程序计数器、虚拟机栈、本地方法栈
开销小创建/切换开销远小于进程(无需切换地址空间)
通信简单直接通过共享内存通信(需要同步)

Java 线程的内存结构

这是面试常考的知识点。Java 线程在 JVM 中的内存布局:

┌─────────────────────────────────────────────────────────┐
│                      进程内存空间                        │
│  ┌───────────────────────────────────────────────────┐  │
│  │                    堆内存 (Heap)                   │  │
│  │  - 所有线程共享                                    │  │
│  │  - 对象实例、数组                                  │  │
│  └───────────────────────────────────────────────────┘  │
│  ┌───────────────────────────────────────────────────┐  │
│  │                   方法区 (Method Area)              │  │
│  │  - 所有线程共享                                    │  │
│  │  - 类信息、常量、静态变量                          │  │
│  └───────────────────────────────────────────────────┘  │
│                                                         │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐       │
│  │  线程 1 栈  │ │  线程 2 栈  │ │  线程 3 栈  │       │
│  │  PC Register│ │  PC Register│ │  PC Register│       │
│  │  JVM Stack  │ │  JVM Stack  │ │  JVM Stack  │       │
│  │  Native Stack│ │  Native Stack│ │  Native Stack│     │
│  └─────────────┘ └─────────────┘ └─────────────┘       │
└─────────────────────────────────────────────────────────┘

每个线程独有的区域

  • 程序计数器(PC Register):记录当前执行的字节码行号,线程切换时恢复执行位置
  • 虚拟机栈(VM Stack):存储局部变量、方法调用栈帧
  • 本地方法栈(Native Stack):native 方法的调用栈

线程共享的区域

  • 堆内存:所有对象实例和数组
  • 方法区:类的结构信息、静态变量、字符串常量池

进程 vs 线程:对比一览

对比维度进程线程
定义资源分配的基本单位CPU 调度的基本单位
地址空间独立共享
通信方式IPC(复杂)直接共享(需同步)
创建开销大(MB 级)小(KB 级)
切换开销大(用户态↔内核态)小(寄存器刷新)
安全性隔离,并发问题少共享,并发问题多
独立性完全独立依赖进程存在

为什么 Java 使用线程而不是进程?

  1. 性能优先:线程切换开销远小于进程,适合高并发场景
  2. 资源共享:多线程可以方便地共享进程资源
  3. 响应更快:一个线程阻塞不影响其他线程
  4. 模型天然契合:Java 程序通常是多任务的(UI、网络、计算),线程是最自然的抽象

实际应用场景

进程的使用场景

  • 隔离构建:Maven/Gradle 每次构建是独立的进程
  • 外部程序调用:调用系统命令、第三方程序
  • 服务隔离:微服务架构中不同服务是独立进程

线程的使用场景

  • Web 服务器:Tomcat 用线程池处理并发请求
  • 异步处理:发送邮件、记录日志可以放到独立线程
  • 并行计算:大数据处理、图像处理分片并行
  • GUI 事件处理:Swing/Android 中 UI 线程和后台线程分离
java
// Web 服务器的线程模型示例
public class SimpleServer {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(8080);
        while (true) {
            // 每收到一个请求,就创建一个线程处理
            Socket socket = ss.accept();
            new Thread(() -> handleRequest(socket)).start();
        }
    }
}

留给你的思考题

进程和线程的根本区别是什么?

如果有两个线程同时修改同一个静态变量 static int counter = 0,会发生什么?

这种「同时修改」的问题,有一个专有名词,叫什么?


面试追问方向:

  • 进程间通信有哪些方式?各自优缺点是什么?
  • 线程切换为什么比进程切换快?
  • Java 中如何获取当前线程?Thread.currentThread() 返回的是什么?

基于 VitePress 构建