【Java多线程与高并发系列】第2讲:核心概念扫盲:进程 vs. 线程

🚀 系列导读: 在上一讲中,我们探讨了为什么多线程能让程序"飞起来"。 本讲我们继续深入底层,从 进程与线程的区别 讲起,一路探索到 Thread 源码实现机制 ,再到 Java 新时代的虚拟线程(Virtual Thread) 。 这是一讲打通"从概念到源码再到未来"的关键课程。


💡 一、进程 vs. 线程:操作系统视角理解

我们首先明确两个概念:

  • 进程(Process) :系统资源(内存、文件、句柄)的基本分配单位。
  • 线程(Thread) :CPU 调度和执行的最小单位。

🧠 举个例子:

当你启动 IDEA(或任何 Java 程序)时:

  • 操作系统会创建一个独立的"进程",分配独立的内存空间;

  • 在这个进程内部,Java 虚拟机会再创建多个线程,比如:

    • 主线程(main)
    • GC线程
    • JIT编译线程
    • Finalizer线程 等等。

可以理解为👇

"进程是容器,线程是执行者。"


🧩 二、Java 中线程的本质:系统线程 vs 虚拟线程

很多人以为 new Thread() 就是 Java 自己实现的"线程",但实际上:

Java 中的线程是由操作系统内核支持的"系统级线程"。


⚙️ 1️⃣ 创建线程的系统过程

当你在 Java 中执行:

java 复制代码
new Thread(() -> System.out.println("Hello")).start();

底层流程如下(以 HotSpot JVM 为例):

  1. Java 层调用 Thread.start()
  2. start() 方法是一个 native 方法 ,最终调用到 JVM 的 JVM_StartThread()
  3. JVM_StartThread() 内部再调用 pthread_create()(Linux)CreateThread()(Windows)
  4. 操作系统为该线程分配 内核线程(Kernel Thread)
  5. 操作系统的调度器(Scheduler) 决定线程的实际执行顺序。

📘 简单来说:

每一个 Java 线程都映射到一个 系统级线程。 操作系统负责线程调度,而非 JVM。


🧩 2️⃣ Thread 源码深度解析

我们看一下 JDK 源码片段(以 JDK 17 为例):

java 复制代码
public class Thread implements Runnable {
    private Runnable target;

    public synchronized void start() {
        if (started)
            throw new IllegalThreadStateException();
        started = true;
        start0();  // native 方法
    }

    private native void start0();
}

💡 start0() 是一个 JNI 本地方法,最终由 JVM 调用系统的底层线程 API 来完成线程创建。


🔍 3️⃣ 调度机制:Java 不负责调度!

Java 不负责决定哪个线程先执行。 线程的调度完全由操作系统控制,通常使用 时间片轮转(Time Slice Round-Robin)优先级调度

这也是为什么:

java 复制代码
Thread t1 = new Thread(...);
Thread t2 = new Thread(...);
t1.start();
t2.start();

两者执行顺序 不可预测


⚙️ 三、Java 创建线程的四种方式(回顾 + 扩展)

方式 实现 特点
① 继承 Thread 重写run()方法 简单直观,但不推荐
② 实现 Runnable 实现任务与线程分离 更灵活,可被多个线程共享
③ 实现 Callable + FutureTask 可返回结果与抛异常 适合异步计算
④ 使用线程池 ExecutorService 线程复用、性能最佳 企业级开发首选

🌟 小贴士:

在实际项目中,推荐使用 线程池(ExecutorService)

  • 避免频繁创建系统线程;
  • 控制最大并发数;
  • 支持任务排队、拒绝策略等。

💻 四、实战:最简多线程示例

java 复制代码
public class HelloThread {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println("子线程:" + Thread.currentThread().getName());
        });
        t.start();
        System.out.println("主线程:" + Thread.currentThread().getName());
    }
}

可能输出:

css 复制代码
主线程:main
子线程:Thread-0

⚠️ 执行顺序不确定,这正是多线程并发执行的体现。


🌙 五、扩展:守护线程 (Daemon Thread)

在 Java 中,线程分为两类:

类型 说明 示例
用户线程 程序的主要执行逻辑 主线程、业务线程
守护线程 服务于用户线程 GC线程、后台监控线程

当所有用户线程执行完毕后,JVM 会自动退出,即使守护线程仍在运行。

java 复制代码
public class DaemonExample {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (true) {
                System.out.println("守护线程运行中...");
                try { Thread.sleep(300); } catch (Exception ignored) {}
            }
        });
        t.setDaemon(true);
        t.start();
        Thread.sleep(1000);
        System.out.println("主线程结束");
    }
}

🧬 六、Java 新时代:虚拟线程(Virtual Thread)

⚡ Java 19 引入的 Project Loom,正式带来了虚拟线程(Virtual Thread)

🌍 1️⃣ 虚拟线程是什么?

虚拟线程是一种 由 JVM 而非操作系统管理 的轻量级线程。

  • 每个虚拟线程不再映射为一个系统级线程;
  • 数百万个虚拟线程可共享少量平台线程;
  • 调度由 JVM 自行管理,而非 OS;
  • 大幅降低线程切换成本。

🧩 2️⃣ 使用方式(JDK 21 示例)

java 复制代码
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 10).forEach(i -> 
        executor.submit(() -> {
            System.out.println("任务 " + i + " 在线程:" + Thread.currentThread());
            Thread.sleep(1000);
            return i;
        })
    );
}

输出(部分):

ruby 复制代码
任务 0 在线程:VirtualThread[#1]/runnable@ForkJoinPool
任务 1 在线程:VirtualThread[#2]/runnable@ForkJoinPool
...

💡 特点:

  • 启动速度极快;
  • 切换几乎无成本;
  • IO 阻塞时自动挂起,不占系统线程。

🔍 3️⃣ 对比总结

特性 平台线程(传统) 虚拟线程(Loom)
底层实现 操作系统内核线程 JVM 管理
数量级 数千个 上百万个
切换成本 高(系统调用) 低(用户态切换)
适用场景 计算密集型任务 IO 密集型任务

📘 虚拟线程不是取代平台线程,而是补充。 主线程、GC线程依然是平台线程,但业务层可大量使用虚拟线程。


📖 七、小结

知识点 核心理解
进程 vs 线程 线程是进程的执行单元,共享同一内存
Java线程实现 每个 Java Thread 对应一个 OS 线程
调度机制 Java 不负责调度,由操作系统完成
Thread源码 start()调用 nativestart0()创建系统线程
守护线程 不会阻止 JVM 退出
虚拟线程 JVM 自管理的轻量级线程,可达百万级并发

✅ 思考与预告

💭 思考题: 如果虚拟线程可以创建上百万个,它是否意味着我们再也不需要线程池?
🔜 下一讲预告: 第3讲:线程的生命周期与状态转换 ------ 揭开 NEW → RUNNABLE → BLOCKED 的完整生命周期。

相关推荐
摸鱼的春哥4 分钟前
春哥的Agent通关秘籍07:5分钟实现文件归类助手【实战】
前端·javascript·后端
Victor35621 分钟前
MongoDB(2)MongoDB与传统关系型数据库的主要区别是什么?
后端
JaguarJack22 分钟前
PHP 应用遭遇 DDoS 攻击时会发生什么 从入门到进阶的防护指南
后端·php·服务端
BingoGo22 分钟前
PHP 应用遭遇 DDoS 攻击时会发生什么 从入门到进阶的防护指南
后端
Victor35623 分钟前
MongoDB(3)什么是文档(Document)?
后端
牛奔2 小时前
Go 如何避免频繁抢占?
开发语言·后端·golang
想用offer打牌7 小时前
MCP (Model Context Protocol) 技术理解 - 第二篇
后端·aigc·mcp
KYGALYX9 小时前
服务异步通信
开发语言·后端·微服务·ruby
掘了9 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
爬山算法9 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate