2026年7月11日 · 5 分钟阅读

Java 并发编程入门:线程、进程、上下文切换与线程生命周期

梳理 Java 多线程基础,包括进程和线程、并发与并行、线程上下文切换、线程状态,以及 Java 线程到操作系统线程的执行链路。

并发编程是高性能 Java 应用绕不开的一关。Tomcat、MySQL、Redis、Elasticsearch、连接池、线程池,本质上都大量使用了多线程。

学并发,不是为了多写几个 new Thread(),而是为了理解多个任务同时运行时,数据为什么会乱、线程为什么会卡、性能为什么会下降。

什么是多线程

多线程是指一个程序中同时存在多个执行路径。它可以让程序更好地利用 CPU、IO 等资源,提高整体处理能力。

但多线程也带来新问题:

  • 多个线程同时修改共享数据。
  • 线程之间看不到彼此的修改。
  • 代码执行顺序和编写顺序不完全一致。
  • 线程创建和切换本身也有成本。

并发编程就是编写多线程代码,并解决多线程带来的正确性和性能问题。

进程和线程

进程是操作系统分配资源的基本单位,可以理解为正在运行的一个应用程序。

线程是进程中的执行单元,一个进程可以包含多个线程。

对比项进程线程
内存空间独立同一进程内线程共享堆和方法区
创建成本较高较低
通信成本较高,需要 IPC较低,可共享进程内数据
隔离性
崩溃影响通常影响单个进程一个线程异常可能影响整个进程

线程共享堆和方法区,但每个线程都有自己的虚拟机栈、本地方法栈和程序计数器。

并发和并行

并发和并行经常被混用,但它们不是一回事。

概念含义
并发同一时间段内多个任务都在推进,不一定同时执行
并行同一时刻多个任务真正同时执行

并发是一种处理多任务的能力,并行是多核 CPU 提供的同时执行能力。

单核 CPU 也能并发,因为它可以通过时间片快速切换线程;多核 CPU 才能真正让多个线程在同一时刻并行运行。

线程上下文切换

CPU 不会一直执行同一个线程。操作系统会把 CPU 时间片分给不同线程,当线程时间片用完、阻塞或被更高优先级任务抢占时,就会发生上下文切换。

一次线程上下文切换通常包含:

  1. 保存当前线程的 CPU 寄存器、程序计数器等状态。
  2. 选择下一个要运行的线程。
  3. 恢复下一个线程之前保存的上下文。
  4. 跳转到该线程上次暂停的位置继续执行。

上下文切换不是免费的。线程过多时,CPU 可能把大量时间花在切换上,而不是执行业务代码。

线程越多越好吗

线程不是越多越好。

线程过多会带来几个问题:

  • 栈内存占用增加。
  • 上下文切换成本升高。
  • 锁竞争更激烈。
  • 调度变复杂,响应时间抖动。

对于 CPU 密集型任务,线程数通常接近 CPU 核数更合理;对于 IO 密集型任务,可以适当多一些线程,因为线程经常等待 IO。

线程生命周期

Java 线程常见状态包括:

状态说明
NEW线程已创建,但还没有调用 start()
RUNNABLE可运行状态,可能正在运行,也可能等待 CPU 调度
BLOCKED等待进入同步代码块或同步方法
WAITING无限期等待其他线程唤醒
TIMED_WAITING限时等待
TERMINATED线程执行结束

RUNNABLE 不等于一定正在执行。它只是说明线程具备运行资格,具体什么时候运行由操作系统调度。

sleep、wait 和 yield

几个常见方法容易混淆。

方法作用是否释放锁
Thread.sleep()当前线程休眠指定时间不释放锁
Object.wait()当前线程等待其他线程通知释放锁
Thread.yield()当前线程让出 CPU 调度机会不释放锁
Thread.interrupt()中断线程,设置中断标记不直接停止线程

interrupt() 不是强制杀死线程,而是一种协作式中断机制。线程需要自己检查中断状态并决定如何退出。

Java 线程如何启动

调用 Thread.start() 后,Java 线程不是直接在 Java 层执行 run()。它背后会进入 JVM 的 native 方法,再创建操作系统线程。

可以简化理解为:

Thread.start()
  -> native start0()
  -> JVM_StartThread
  -> 创建 JavaThread
  -> 创建 OS 线程
  -> OS 线程执行入口
  -> 回调 Java run()

这说明线程创建是比较重的操作,涉及 JVM 和操作系统协作。频繁创建线程会影响性能,这也是线程池存在的原因。

线程安全从哪里来

线程安全问题通常来自共享可变状态。

比如多个线程同时卖票:

if (ticket > 0) {
    ticket--;
}

这段逻辑看起来简单,但判断和扣减不是一个不可分割的操作。多个线程可能同时看到同一张票还存在,最终导致重复售卖或卖出不存在的票。

要解决线程安全问题,通常有三类思路:

  • 避免共享:每个线程使用自己的数据。
  • 不可变:共享对象不可修改。
  • 同步控制:用锁、原子类、并发容器等保护共享数据。

小结

Java 并发入门要先抓住几个基础判断:

  1. 进程是资源单位,线程是执行单位。
  2. 并发不等于并行。
  3. 线程切换有成本,线程不是越多越好。
  4. Java 线程最终要映射到操作系统线程。
  5. 线程安全问题来自共享可变状态。

后续学习 JMM、synchronizedvolatile、CAS、AQS 和线程池,都是在回答同一个问题:多个线程怎样正确、高效地协作。