likes
comments
collection
share

升级到 Java 21 是值得的

作者站长头像
站长
· 阅读数 5

升级到 Java 21 是值得的

又到了一年中的这个时候——New Relic 的年度“State of the Java Ecosystem”调查结果出来了,我一如既往地深入研究了它。虽然我认为该报告做得很好并且提出了很好的问题,但我对有多少 Java 开发人员正在使用低版本感到沮丧。

您使用的是 Java 21 吗?确实应该使用了。

在开始调查之前,作为一名 Java 爱好者,我想谈谈我最喜欢的关于 Java 21 的一些事情。

首先我要说的是,Spring Boot 3.x 是当前 Java 虚拟机 (JVM) 上最流行的服务器端技术栈,至少需要 Java 17。它不支持 Java 8,这是第二个版本。根据调查,最常用的版本。

我很高兴看到 Java 17 的采用进展相对较快,但您确实应该使用 Java 21。Java 21 比 Java 8 好得多。它在所有方面都在技术上优越。它更快、更安全、更易于操作、性能更高、内存效率更高。

道德上也很优越。当您的孩子发现您在生产中使用 Java 8 时,您不会喜欢他们眼中流露出羞愧和悲伤的表情。

做正确的事,成为你希望在世界上看到的改变:使用 Java 21。它充满了优点,基本上是自 Java 7 以来的一种全新语言:Lambdas,Multiline strings。Smart switch expressions。 var 。Pattern matching。Named tuples(在 Java 中称为 records )。

当然,最重要的是虚拟线程。虚拟线程是一件大事。它们提供了与 async / await 或 suspensions 相同的优点,但没有其他语言中冗长代码。

是的,你明白我的意思了。与其他语言相比,Java 的虚拟线程提供了更好的解决方案,并且代码更少。

如果你不知道我在说什么,并且使用其他语言,那么你现在会很生气。java?比您最喜欢的语言更简洁?不可能的!但我并没有错。

为什么虚拟线程很重要

要了解virtual threads,您需要了解创建它们是为了解决的问题。如果您还没有体验过虚拟线程,那么它们有点难以描述。我会尽力。

Java 有阻塞操作——比如 Thread.sleep(long)InputStream.readOutputStream.write 。如果您调用其中一个方法,程序将不会前进到下一行,直到这些方法完成它们正在做的事情并返回。

大多数网络服务都是 I/O 密集的,这意味着它们将大部分时间花在输入和输出方法上,例如 InputStream.readOutputStream.write

任务提交到线程池中却没有更多线程的服务是很常见的,但仍然无法返回响应,因为所有现有线程都在等待某些 I/O 操作发生,例如跨线程的 I/O HTTP 边界、数据库或消息队列的 I/O。

有多种方法可以解锁 I/O。您可以使用 java.nio ,它非常复杂,会引起焦虑。您可以使用reactive式编程,它的工作原理是范式的(paradigmatically),但它是对整个代码库的完整重构。

因此,我们的想法是:如果编译器知道您何时执行了可能会阻塞的操作(例如 InputStream.read )并重新排序代码的执行,这不是很好吗?因此,当您执行阻塞操作时,等待代码将从当前执行线程移出,直到阻塞操作完成,然后在准备好恢复执行后将其放回另一个线程。

这样,您就可以继续使用阻塞语义。第一行在第二行之前执行。这提高了可调试性和可扩展性。您不再垄断线程只是为了在等待某些事情完成时浪费它们。这将是两全其美:非阻塞 I/O 的可扩展性与更简单的阻塞 I/O 的明显简单性、可调试性和可维护性。

许多其他语言,如 Rust、Python、C#、TypeScript 和 JavaScript,都支持 async / await 。 Kotlin 支持 suspend 。这些关键字提示运行时您将要做一些阻塞的事情,并且它应该重新排序执行。这是一个 JavaScript 示例:

async function getCustomer(){ /* call a database */ }
const result = await getCustomer();

问题症结在于要调用 async 函数,还必须位于 async 函数中:

async function getCustomer(){ /* call a database */ }

async function main(){ 
  const result = await getCustomer();
}

async 关键字 是病毒式的。它蔓延开来。最终,你的代码会陷入 async / await 的泥潭——你为什么在任何可能的地方使用async/await呢?因为,它比使用低级、非阻塞 I/O 或反应式编程要好,但也只是勉强好。

Java 提供了一种更好的方法。只需为您的线程使用不同的工厂方法即可。

如果您使用 ExecutorService 创建新线程,请使用创建虚拟线程的新版本。

var es = Executors.newVirtualThreadPerTaskExecutor(); 
// ^- this is different and you'll probably only do it once 
// or twice in a typical application
var future = es.submit(() -> System.out.println("hello, virtual threads!"));

如果您直接在较低级别创建线程,则使用新的工厂方法:

// this is different
var thread = Thread.ofVirtual().start(() -> System.out.println("hello, virtual threads!"));

您的大部分代码保持完全不变,但现在您的可扩展性得到了显着提高。如果您创建数百万个线程,运行时不会喘息。我无法预测您的结果会是什么,但您很有可能不再需要运行给定服务的几乎同样多的实例来处理负载。

如果您使用的是 Spring Boot 3.2(您是,不是吗?),那么您甚至不需要执行任何操作。只需在 application.properties 中指定 spring.threads.virtual.enabled=true ,然后向管理层请求加薪,费用由大幅降低的云基础设施成本支付。

并非每个应用程序都可以在技术上实现跨越,但其中绝大多数可以而且应该。

使用情况报告分析

最后,这让我回到了 New Relic 报告。不要误会我的意思:它做得非常好,值得一读。就像莎士比亚悲剧一样,它写得很好,讲述了一个悲伤的故事。

有一个完整的部分证实了显而易见的事实:天空是蓝色的,云彩无处不在。在容器中部署工作负载似乎是主流模式,受访者表示 70% 的 Java 工作负载使用容器。坦白说,我很惊讶它这么低。

同样令人感兴趣的是从单核配置转向多核的趋势。根据调查,30% 的容器化应用程序正在使用 Java 9 的 -XX:MaxRAMPercentage 标志,该标志限制了 RAM 使用。 G1 是最流行的垃圾收集器。一切都很好。

当涉及到 Java 版本时,该报告发生了悲剧性的转变。超过一半的应用程序(56%)在生产中使用 Java 11,而 2022 年这一比例为 48%。Java 8(十年前的 2014 年发布)紧随其后,近 33% 的应用程序在生产中使用它。根据调查,三分之一的应用程序仍在使用 Java 版本,该版本在《Flappy Bird》游戏被下架、《冰桶挑战》横扫 Vine、《Ellen DeGeneres 奥斯卡》自拍照火爆的同一年推出。

多个用户使用 Amazon 的 OpenJDK 分发版。该报告表明,这是因为甲骨文暂时为其发行引入了更严格的许可。但我想知道其中有多少只是 Amazon Web Services(最多产的基础设施即服务 (IaaS) 供应商)上 Java 工作负载默认分布的函数。自几年前推出以来,该发行版已受到广泛欢迎。 2020年,它的市场份额为2.18%,现在则为31%。如果这么多人可以如此迅速地迁移到完全不同的发行版,那么他们应该能够使用同一发行版的新版本,不是吗?

我想,趋势中还是有一些希望的。 Java 17 用户采用率一年内增长了 430%。因此,也许我们会在 Java 21 中看到类似的数字——Java 21 已经全面发布近六个月了。

你还在等什么?

正如我在 Voxxed Days 的演讲中所说,我相信现在是成为 Java 和 Spring Boot 开发人员的最佳时机。 Java 和 Spring 开发人员拥有最好的玩具。我什至还没有提到 GraalVM 本机映像,它可以显着缩短给定 Java 应用程序的启动时间并减少内存占用。这已经与 Java 21 完美配合。

这些东西就在这里,它们太棒了。能否实现这一跳跃取决于我们。这并不难。试试看。

安装 SDKMan,运行 sdk install java 21.0.2-graalce 然后运行 ​​ sdk default java 21.0.2-graalce 。这将为您提供 Java 21 和 GraalVM 本机映像编译器。访问 Spring Initializr,这是我在网络上第二喜欢的地方(仅次于生产),网址为 start.spring.io。配置一个新项目。选择 Java 21(自然!)。添加 GraalVM Native Support 。添加 Web 。点击 Generate 按钮并将其加载到您的 IDE 中。在 application.properties 中指定 spring.threads.virtual.enabled=true 。创建一个简单的 HTTP 控制器:

@Controller
class Greetings {

 @GetMapping("/hi")
 String hello(){ 
    return "hello, Java 21!";
 }
}

将其编译为 GraalVM 本机映像: ./gradlew nativeCompile 。运行 build 文件夹中的二进制文件。

现在,您已经有了一个应用程序,该应用程序只占用非 GraalVM 本机映像所需 RAM 的一小部分,并且还能够扩展到每秒更多的请求。简单,而且令人惊奇。


原文地址:We CAN Have Nice Things: Upgrading to Java 21 Is Worth It - The New Stack