Java19带来的虚拟线程是如何将性能提升十倍的?基本概念我们都知道Java中的线程与操作系统的内核线程是一对一的。Java线程的调度实际上依赖于操作系统的内核线程,这就导致我们的线程切换和运行中需要进行上下文切换。而且会消耗大量的系统资源。同时,我们也知道机器的资源是昂贵且有限的。我们不能也不能肆无忌惮地创建线程,所以线程往往成为我们系统的瓶颈。为了解决这个问题,Java19提出了虚拟线程的概念。为了区分,前面的线程称为平台线程。需要注意的是,虚拟线程并不是用来直接替代平台线程的。建议将虚拟线程置于平台线程之上。一个平台线程可以对应多个虚拟线程。同时,一个平台线程仍然与内核线程一一对应。于是,上面的架构就变成了如下,一个VT代表一个虚拟线程。如果有熟悉GO语言的朋友,你会觉得Java中的虚拟线程和GO中的Goroutines很像,确实如此,所以语言相通。例如,这里我们使用平台线程和虚拟线程来测试一个案例,看看它们的耗时和性能如何。测试分为以下几个步骤,我们依次来看一下。注意下面的测试代码是在Java19版本运行的。在平台线程模式下,我们使用JDK自带的线程池Executors.newCachedThreadPool()创建线程池,执行某些数据任务。我们通过传入参数来控制任务的个数,方便后续通过main函数调用。publicstaticvoidplatformThread(intsize){longl=System.currentTimeMillis();try(varexecutor=Executors.newCachedThreadPool()){IntStream.range(0,size).forEach(i->{executor.submit(()->{Thread.sleep(Duration.ofSeconds(1));//System.out.println(i);返回i;});});}System.out.printf("运行时间:%dms\n",System.currentTimeMillis()-l);}虚线程的方式虚线程的代码和上面的代码很相似,代码如下。可以看出在代码层面和上面的唯一区别就是Executors.newCachedThreadPool()这一行变成了Executors.newVirtualThreadPerTaskExecutor(),代表创建的虚拟线程。publicstaticvoidvirThread(intsize){longl=System.currentTimeMillis();try(varexecutor=Executors.newVirtualThreadPerTaskExecutor()){IntStream.range(0,size).forEach(i->{executor.submit(()->{Thread.sleep(Duration.ofSeconds(1));//System.out.println(i);返回i;});});}System.out.printf("运行时间:%dms\n",System.currentTimeMillis()-l);}监控正在运行的线程以上两种方式都是创建线程池来提交任务,但是我们不知道创建了多少个线程,所以我们还需要通过下面的代码进行监控。publicstaticvoidmain(String[]args){ScheduledExecutorServicescheduledExecutorService=Executors.newScheduledThreadPool(1);scheduledExecutorService.scheduleAtFixedRate(()->{ThreadMXBeanthreadBean=ManagementFactory.getThreadMXBean();ThreadInfo[=threadInfofalse);长计数=Arrays.stream(threadInfo).count();System.out.println(count+"操作系统线程");},1,1,TimeUnit.SECONDS);intsize=100000;//platformThread(size);虚拟线程(大小);}通过另一个线程池开启一个线程信息监控线程,每秒输出当前运行线程数。这里要注意,如果上面的代码在IDEA中提示错误,找不到类,如下图,我们可以将鼠标放在上面进行修复。您还可以在编译器>Java编译器中手动将编译参数添加到您自己的模块中设置-parameters--add-modulesjava.management--enable-preview。把上面三段跑在一起就是一个完整的案例。如果这时候上面的代码正常的话,运行不出意外就会出现下面的错误。这是因为Java19中的虚拟线程特性还处于预览阶段,不能直接使用。我们需要在启动参数上配置--enable-preview参数才能正常测试。如下图,不同版本的IDEA可能显示的位置不同,但都是配置了VM参数。只是寻找它。配置完成后,再次运行得到如下结果。可以看出,当size为100000时,虚拟线程只创建了12个平台线程,仅用了2523ms就完成了整个任务。但是当我们运行平台线程的方法时,会发现在同样大小的情况下,创建了上千个平台线程,会因为操作系统的资源已经耗尽而触发OOM,所以可以可见,虚拟线程的性能远高于平台线程。YYDS![]为了避免OOM,我们也可以将代码中的Executors.newCachedThreadPool()方法改成Executors.newFixedThreadPool(xxx)。这样虽然可以避免创建大量线程造成的OOM,但是任务的执行时间会变长。啊范主测试,size为10000,配置500个线程,一共耗时20276ms,数据量小十倍时,耗时增加十倍。性能可想而知,有兴趣的朋友可以自行尝试。
