Java 21——虚拟线程

简介: Java引入虚拟线程以解决传统线程的资源限制和扩展性问题。传统线程直接绑定操作系统线程,资源消耗大且难以高效处理大量并发任务。虚拟线程轻量高效,由JVM管理,大幅降低资源开销,提升应用扩展性。例如,使用虚拟线程可轻松处理数十万个并发请求,而不会触发资源耗尽错误,显著提升性能和稳定性。

Java 中引入虚拟线程是为了解决与传统操作系统 (OS) 线程相关的某些限制和挑战。为了了解虚拟线程的需求,让我们深入研究线程在 Java 中的内部工作方式以及为什么需要考虑使用操作系统线程。

01

在 Java 中,当您创建线程时,它直接与操作系统线程绑定。这意味着每个 Java 线程都会消耗一个操作系统线程,并且此连接有一些含义:

1.资源限制:就像文件句柄、端口和驱动程序一样,操作系统对其可以有效处理的线程数量也有限制。如果应用程序创建太多线程,可能会导致资源耗尽。

2.可扩展性:当应用程序使用大量操作系统线程时,它们可能很难有效地扩展。在需要处理大量并发任务的情况下(例如在 Web 服务器中),这可能会成为问题。

现在,您可能想知道谁真正使用线程,您为什么要关心?

许多应用程序,尤其是使用 Spring Boot 构建的 Web 服务器,严重依赖线程来进行并发任务处理。这些线程负责处理传入的请求,正如您正确指出的那样,跟踪每个线程正在执行的操作可能具有挑战性。

考虑这样一个场景:您的 Spring Boot 应用程序正在服务大量并发请求。这些请求可能会执行各种操作,例如进行数据库查询、等待 Web 服务的响应或循环读取大文件。问题在于,分配给请求的每个线程都专门关注该请求,因此很难有效地利用可用资源。

例如,考虑以下场景

我们将创建 5000 个线程来调用名为 handleRequest 的方法

List<Thread> threads = new ArrayList<>();  
  
for (int i = 0; i < 5000; i++) {  
  
  
// create a thread  
Thread thread = new Thread(() -> {  
     handleRequest();  
});
// start the thread  
thread.start();  
// add the thread to the list  
threads.add(thread);  
System.out.println("Thread " + thread.threadId()+ " started");  
}  
// wait for all the threads to finish  
  
for (Thread thread : threads) {  
  
thread.join();  
  
}
  • 打印一条消息并等待 3 秒
void handleRequest() {  
  
try {  
  
System.out.println("Handling request on Thread " + Thread.currentThread().threadId() + " started");  
Thread.sleep(3000);  
System.out.println("Handling request on Thread " + Thread.currentThread().threadId() + " completed");  
}
}

期望一切都可能运作良好,但是当它运行时

Exception in thread "main" java.lang.RuntimeException: 
java.lang.OutOfMemoryError: unable to create native thread: 
possibly out of memory or process/resource limits reached
 at virtualThread.ClassicThread.startClassicalThreads(ClassicThread.java:69)
 at virtualThread.ClassicThread.main(ClassicThread.java:34)

您可以看到 OutOfMemoryError ,因为达到了资源限制。

让我们迁移相同的代码,只需更改为使用虚拟线程

而不是这个

Thread thread = new Thread(() -> {  
    handleRequest();  
});

用这个

Thread thread = Thread.ofVirtual().unstarted(() -> {  
    handleRequest();  
});

Thread.ofVirtual() 是创建一个虚拟线程,我们使用回调调用未启动的方法,这样线程就会被创建,我们将手动启动它

运行程序

现在您将看到以下输出

All 5000 threads completed successfully

既然它运行良好,为什么我们不能将循环从500增加到5,00,000

for (int i = 0; i < 500000; i++) {
    // create a thread
  Thread thread = Thread.ofVirtual().unstarted(() -> {  
      handleRequest();  
  });
    // start the thread
  thread.start();
  //...........

运行程序

输出将是

All 500000 threads completed successfully



02

以下是虚拟线程的主要优点的概述:

1.资源效率:虚拟线程不直接与操作系统线程相关,这意味着它们不会像传统线程那样消耗操作系统资源。这使得它们非常高效,正如您能够运行 500,000 个虚拟线程而不会遇到资源限制所证明的那样。

2.可扩展性:虚拟线程是轻量级的,可以更轻松地扩展应用程序,而无需担心管理大量操作系统线程的开销。这在 Web 服务器等场景中尤其重要,高效处理大量并发请求至关重要。

3.减少管理费用:与操作系统线程相比,虚拟线程的开销较低,因为它们由 Java 虚拟机 (JVM) 管理。这可以提高性能并降低遇到内存不足错误等问题的风险。

因此,如果您的网络服务器花了 3 秒来处理一个请求,那么在不启动新服务器的情况下处理 500,000 个请求不是很酷吗?

03

完整源代码

import java.util.ArrayList;
import java.util.List;
public class ClassicThread {
    void handleRequest() {
        try {
            // do some work with Request
            System.out.println("Handling request on Thread " + Thread.currentThread().threadId() + "  started");
            Thread.sleep(3000);
            System.out.println("Handling request on Thread " + Thread.currentThread().threadId() + "  completed");
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    public static void main(String[] args) {
        System.out.println("Hello, World!, Classic Thread Demo");
        startClassicalThreads(5000);
    }
    public static void startClassicalThreads(int n) {
        try {
            List<Thread> threads = new ArrayList<>();
            for (int i = 0; i < n; i++) {
                // create a thread
                Thread thread = new Thread(() -> {
                    new ClassicThread().handleRequest();
                });
                // start the thread
                thread.start();
                // add the thread to the list
                threads.add(thread);
                System.out.println("Thread " + thread.threadId()+ " started");
            }
            // wait for all the threads to finish
            for (Thread thread : threads) {
                thread.join();
            }
            System.out.println("All "+n+" threads completed successfully");
        } catch (Throwable e) {
            System.err.println("Error in completing threads");
            throw new RuntimeException(e);
        }
    }
}
import java.util.ArrayList;
import java.util.List;
public class VirtualThread {
    void handleRequest() {
        try {
            // do some work with Request
            System.out.println("Handling request on Thread " + Thread.currentThread().threadId() + "  started");
            Thread.sleep(3000);
            System.out.println("Handling request on Thread " + Thread.currentThread().threadId() + "  completed");
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    public static void main(String[] args) {
        System.out.println("Hello, World!, Virtual Thread Demo");
        startVirtualThreads(500000);
    }
    public static void startVirtualThreads(int n) {
        try {
            List<Thread> threads = new ArrayList<>();
            for (int i = 0; i < n; i++) {
                // create a thread
                Thread thread = Thread.ofVirtual().unstarted(() -> {
                    new VirtualThread().handleRequest();
                });
                // start the thread
                thread.start();
                // add the thread to the list
                threads.add(thread);
                System.out.println("Thread " + thread.threadId()+ " started");
            }
            // wait for all the threads to finish
            for (Thread thread : threads) {
                thread.join();
            }
            System.out.println("All "+n+" threads completed successfully");
        } catch (Throwable e) {
            System.err.println("Error in completing threads");
            throw new RuntimeException(e);
        }
    }
}
相关文章
|
3月前
|
数据采集 搜索推荐 Java
Java 大视界 -- Java 大数据在智能教育虚拟学习环境构建与用户体验优化中的应用(221)
本文探讨 Java 大数据在智能教育虚拟学习环境中的应用,涵盖多源数据采集、个性化推荐、实时互动优化等核心技术,结合实际案例分析其在提升学习体验与教学质量中的成效,并展望未来发展方向与技术挑战。
|
3月前
|
数据采集 存储 前端开发
Java爬虫性能优化:多线程抓取JSP动态数据实践
Java爬虫性能优化:多线程抓取JSP动态数据实践
|
1月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
122 1
|
1月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
138 1
|
4月前
|
Java API 调度
从阻塞到畅通:Java虚拟线程开启并发新纪元
从阻塞到畅通:Java虚拟线程开启并发新纪元
345 83
|
2月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
194 16
|
2月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
|
3月前
|
缓存 并行计算 安全
关于Java多线程详解
本文深入讲解Java多线程编程,涵盖基础概念、线程创建与管理、同步机制、并发工具类、线程池、线程安全集合、实战案例及常见问题解决方案,助你掌握高性能并发编程技巧,应对多线程开发中的挑战。
|
存储 安全 Java
深入理解Java并发编程:线程安全与锁机制
【5月更文挑战第31天】在Java并发编程中,线程安全和锁机制是两个核心概念。本文将深入探讨这两个概念,包括它们的定义、实现方式以及在实际开发中的应用。通过对线程安全和锁机制的深入理解,可以帮助我们更好地解决并发编程中的问题,提高程序的性能和稳定性。
|
存储 安全 Java
解锁Java并发编程奥秘:深入剖析Synchronized关键字的同步机制与实现原理,让多线程安全如磐石般稳固!
【8月更文挑战第4天】Java并发编程中,Synchronized关键字是确保多线程环境下数据一致性与线程安全的基础机制。它可通过修饰实例方法、静态方法或代码块来控制对共享资源的独占访问。Synchronized基于Java对象头中的监视器锁实现,通过MonitorEnter/MonitorExit指令管理锁的获取与释放。示例展示了如何使用Synchronized修饰方法以实现线程间的同步,避免数据竞争。掌握其原理对编写高效安全的多线程程序极为关键。
254 1