0Pricing
Java Academy · 课时

平台线程与虚拟线程

了解各自适用的场景

平台线程与虚拟线程 是 CoddyKit 上的免费 Java Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。

两类线程

Java 现在有两种线程类型。平台线程是围绕 OS 线程的经典封装。虚拟线程轻量且由 JVM 管理。

掌握何时选择哪一种,是编写可扩展并发 Java 程序的核心。

创建平台线程

您可以使用 Thread.ofPlatform() 创建平台线程,它对应于 Thread.ofVirtual()。

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread p = Thread.ofPlatform().name("native-worker").start(() ->
            System.out.println("Platform? " + !Thread.currentThread().isVirtual()));
        p.join();
    }
}

开销对比

平台线程开销较大:

  • 每个线程预留约 1 MB 的栈空间
  • 创建速度慢
  • 数量限制在几千个

虚拟线程开销很低:初始栈很小、创建速度快,可行数量达到数百万。

输入输出密集型工作

如果您的任务大部分时间都在等待网络、磁盘或数据库,虚拟线程的优势会非常明显。

当一个虚拟线程阻塞时,其载体线程会被释放去运行其他线程,因此少量 OS 线程就能服务数千个请求。

CPU 密集型工作

对于始终不会阻塞的纯计算任务,虚拟线程不会带来加速。您会受到核心数的限制。

在这种情况下,使用大小与处理器数量相匹配的固定线程池才是正确的选择。

import java.util.concurrent.*;

public class Main {
    public static void main(String[] args) throws Exception {
        int cores = Runtime.getRuntime().availableProcessors();
        try (ExecutorService cpu = Executors.newFixedThreadPool(cores)) {
            Future<Long> f = cpu.submit(() -> {
                long s = 0;
                for (long i = 0; i < 1_000_000L; i++) s += i;
                return s;
            });
            System.out.println("Cores: " + cores + ", sum: " + f.get());
        }
    }
}

模拟阻塞

这段代码展示了许多虚拟线程同时休眠(作为阻塞式输入输出的替代情形)。请试着想象一下让 10,000 个平台线程做同样的事情。

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    public static void main(String[] args) {
        AtomicInteger done = new AtomicInteger();
        try (ExecutorService exec = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < 10_000; i++) {
                exec.submit(() -> {
                    try { Thread.sleep(50); } catch (InterruptedException e) {}
                    done.incrementAndGet();
                });
            }
        }
        System.out.println("Finished blocking tasks: " + done.get());
    }
}

守护状态

虚拟线程始终是守护线程,不能改为非守护线程。平台线程默认继承创建者的守护状态,但也可以显式设置。

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread v = Thread.ofVirtual().unstarted(() -> {});
        Thread p = Thread.ofPlatform().unstarted(() -> {});
        System.out.println("virtual daemon: " + v.isDaemon());
        System.out.println("platform daemon: " + p.isDaemon());
    }
}

忽略优先级

虚拟线程始终报告 NORM_PRIORITY 优先级,设置优先级不会产生任何影响。调度由 JVM 处理,而不是由 OS 调度器处理。

public class Main {
    public static void main(String[] args) {
        Thread v = Thread.ofVirtual().unstarted(() -> {});
        v.setPriority(Thread.MAX_PRIORITY);
        System.out.println("Priority is still: " + v.getPriority());
    }
}

选择指南

一个实用的经验法则:

  • 有大量任务需要等待输入输出?使用虚拟线程。
  • 有受 CPU 限制的繁重计算?使用平台线程固定线程池。
  • 需要进行操作系统级线程调优(优先级、亲和性)?使用平台线程。

两者可以共存

您不必为整个程序只选择一种线程。典型应用会使用虚拟线程处理请求,同时使用小型平台线程池处理 CPU 密集型部分。

两者共享相同的 Thread 接口,因此可以无缝混用。

迁移提示

迁移输入输出负载较高的服务时,最小的改动是替换执行器:将固定线程池或缓存线程池替换为 newVirtualThreadPerTaskExecutor()。

但请注意线程固定和 ThreadLocal 滥用问题,下一课将介绍这些内容。

快速检验

请选择虚拟线程能带来最明显收益的工作负载。

回顾

您比较了两种线程模型:

  • 平台线程:开销大、由 OS 支持,最适合 CPU 密集型工作和操作系统级调优
  • 虚拟线程:开销低、由 JVM 管理、始终是守护线程,最适合输入输出密集型并发
  • 两者可以共存并共享相同的接口

下一步:线程固定和 ThreadLocals 带来的陷阱。

常见问题解答

「平台线程与虚拟线程」课时是免费的吗?

是的 — 「平台线程与虚拟线程」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。

「平台线程与虚拟线程」这节课中我会学到什么?

了解各自适用的场景 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Java Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「平台线程与虚拟线程」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Java Academy 课中编写并运行代码吗?

能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 什么是虚拟线程
  2. 创建虚拟线程
  3. 平台线程与虚拟线程
  4. 陷阱:固定与 ThreadLocals
← 返回 Java Academy