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); } } }