【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 的完整生命周期。

相关推荐
Lear5 小时前
SpringBoot异步编程
后端
间彧5 小时前
Java LongAdder详解与应用实战
后端
Lear5 小时前
Spring MVC 拦截器与过滤器的区别及执行顺序
后端
Lear5 小时前
SpringMVC之过滤器(Filter)
后端
WoodWall5 小时前
WebServer 02 Reactor模式
c++·后端
Tech有道5 小时前
滴滴面经分享:那个让我差点翻车的Spring面试题!
后端
再吃一根胡萝卜5 小时前
项目结构对比:Flask vs Django
后端
间彧5 小时前
LongAdder和AtomicLong的区别与使用场景选择
后端
间彧5 小时前
LongAdder支持高并发,为什么还要使用AtomicLong,直接使用LongAdder不就行了?(实则不然)
后端