JVM 是一个多线程环境,它通过 java.lang.Thread 提供了对操作系统线程的抽象,但是Java中的线程都只是对操作系统线程的一种简单封装,可以称之为“平台线程”。在某些场景下,它是有一些缺陷的,具体体现在:
代价昂贵:创建平台线程的成本很高。每当创建一个平台线程时,操作系统都必须在堆栈中分配大量内存来存储线程的上下文、原生调用堆栈和Java调用堆栈。
上下文切换成本高:在多线程环境下,需要在不同线程间切换,这种上下文切换会消耗时间和资源。
线程数量有限:Java 线程仅仅只是对操作系统线程的封装,而操作系统线程的数量是有限的,这就限制了Java 同时运行的线程数量,从而限制了应用程序的并发能力。
虚拟线程则是通过平台线程(载体线程)来管理,一个平台线程可以在不同的时间执行不同的虚拟线程(多个虚拟线程挂载在一个平台线程上),当虚拟线程被阻塞或等待时,平台线程可以切换到执行另一个虚拟线程。
优点
非常轻量级:可以在单个线程中创建成百上千个虚拟线程而不会导致过多的线程创建和上下文切换。
简化异步编程:虚拟线程可以简化异步编程,使代码更易于理解和维护。它可以将异步代码编写得更像同步代码,避免了回调地狱。
减少资源开销:由于虚拟线程是由 JVM 实现的,它能够更高效地利用底层资源,例如 CPU 和内存。虚拟线程的上下文切换比平台线程更轻量,因此能够更好地支持高并发场景。
提升性能:在I/O 密集型应用中,虚拟线程能够显著地提升性能。而且由于它们的创建和销毁成本低,能够更加高效地利用系统资源。
虚拟线程并不旨在完全替代传统的操作系统线程,而是作为一个补充。对于需要密集计算和精细控制线程行为的场景,传统线程仍然是主流。
虚拟线程主要针对高并发和高吞吐量,而不是最低延迟。对于需要极低延迟的应用,传统线程可能是更好的选择。
虚拟线程改进了线程的实现方式,但并未改变Java基本的线程模型和同步机制。锁和同步仍然是并发控制的重要工具。
官方提供了以下四种方式创建虚拟线程:
(1)Thread.startVirtualThread()是Thread提供的静态方法,创建一个新的虚拟线程,并立即执行给定的Runnable任务
@Test
public void virtualThreadTest() {
Thread.startVirtualThread(() -> {
// 这里放置你的任务代码
System.out.println("虚拟线程");
});
}
(2)Thread.ofVirtual 是一个流式API,用于构建和配置线程。它提供了设置线程属性(如名称、守护状态、优先级、未捕获异常处理器等)的方法。相比直接使用 Thread 来构建线程,Thread.Builder提供了更多的灵活性和控制力。
@Test
public void virtualThreadTest(){
Thread.ofVirtual()
.name("virtualThreadTest")
.uncaughtExceptionHandler((t,e)-> System.out.println("线程[" + t.getName() + "发生了异常。message:" + e.getMessage()))
.start(()->{System.out.println("虚拟线程");
});
}
(3)ThreadFactory
public class VirtualThreadTest {
public static void main(String[] args) {
CustomThread customThread = new CustomThread();
ThreadFactory factory = Thread.ofVirtual().factory();
Thread thread = factory.newThread(customThread);
thread.start();
}
}
static class CustomThread implements Runnable{
@Override
public void run() {
System.out.println("CustomThread run");
}
}
(4)Executors.newVirtualThreadPerTaskExecutor() 虚拟线程也支持线程池,线程池会给每个任务分配一个虚拟线程
public class VirtualThreadTest {
public static void main(String[] args) {
CustomThread customThread = new CustomThread();
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
executor.submit(customThread);
}
}
static class CustomThread implements Runnable {
@Override
public void run() {
System.out.println("CustomThread run");
}
}
虚拟线程是由JVM调度的,而不是操作系统。它们占用空间小,同时使用轻量级的任务队列来调度,避免了线程间基于内核的上下文切换开销。在密集 IO 的场景下,需要创建大量的平台线程异步处理才能达到虚拟线程的处理速度。因此,在密集 IO 的场景,虚拟线程可以大幅提高线程的执行效率,减少线程资源的创建以及上下文切换。虽然虚拟线程在资源的消耗上面比平台线程少,但是仍然需要合理管理,创建过多的虚拟线程可能会导致内存消耗的增加。通常我们不需要直接使用虚拟线程,像Tomcat、Netty、Spring boot等都已经支持虚拟线程。