
篇首说明: 本文十分冗长, 语言组织混乱, 如果觉得 TLDR, 就直接跳到 关于虚拟线程的总结 部分看要点, 若对总结上中的某些要素点仍有兴趣的话请倒查本文中其他部分的内容. 个人对 Java 虚拟线程的主动研究是为了在项目中更有效的使用它.
关于线程的概要
Java 21 于两年前 2023 年 9 月份放出,它是一个 LTS(long term support) 版本,个人基本就是把 LTS 当作能在正式项目中使用的版本。 Java 21 有几个增进编程体验的特性,像 Sequenced Collections, Record Patterns, 和 Pattern Matching for switch, 而对于性能改进的, 也是 Java 21 最具代表的特性无疑就是 Virtual Threads -- 虚拟线程。本文单列出它来,着重感受一下虚拟线程是什么,以及我们应该如何使用它。
其实在之前的 Java 19, 20 新特性学习 就有一定的笔墨介绍了于 Java 19 引入, Java 20 中尚处于第二次预览的虚拟线程。于其中大致体验了在一台 36 G 物理内存,默认堆内存为 9 G 的情况下, 创建 9000 个线程没问题,但要创建 10000 个线程就 OutOfMemoryError 了。而相同的环境下创建一百万个虚拟线程都没问题,没在继续往下试探了。
其实这种比较是没有意义的, Java 线程对应到平台线程的, 每个线程要至少实实在在的 2M 栈空间, 而一百万个虚拟线程相当于是创建了一百万个 Java 对象而已, 更像是相应数量的 Task, 实际运行时才由载体线程去调度执行 - (注: 后面所提到的载体线程和平台线程是同一个概念).
重新回顾一下何谓虚拟线程,Java 的虚拟线程实现是来自于 Project Loom 项目。与此相关的概念有线程,协程,以及纤程(Fiber),而虚拟线程对应的应该是纤程。
- 线程是操作系统最小的调度单位,每个线程有独立较大的栈空间(比如 2M),内核调度,切换开销大,可有效使用 CPU 多核
- 协程在单个线程内执行,共享线程栈空间或独立小空间,用户态调度,切换开销极小,但无法使用多核
- 纤程,介于线程与协程之间,很小的独立栈,用户态调度,切换开销较小。结合线程池,纤程可在线程间转移,这时岂不是要经内核态调度吗?
之所以把 Java 19 与 20 放一块是因为这两个版本都没有一个算得上正式的特性。都是些预览的,孵化中的,唯有一个支持 Linux 下 RISC-V 指令集与我们基本无关。所以 Java 19 和 Java 20 纯粹的过度版本,根本不该被正式项目采用,在 IntelliJ IDEA 中也是标它们为 No new language features。在我们的实践中正式项目只用 LTS 版。
还是分别从 https://openjdk.org/projects/jdk/19/ 和 https://openjdk.org/projects/jdk/20/ 抓关注点
从上面可以挑几个稍加了解,详细的介绍应该在学习 Java 21 时。它们是 Read MoreJava 19 新特性 Java 20 新特性
有了 AI 是不是就用不着了解语言特性本身呢?用 Vibe Coding 难道就无所不能呢?如果是的话那些找工作的也就无需刷 LeetCode 了。试想 Vibe Coding 产生了成堆的代码,即使创建了 Pull Request, 也不是给人 Review 的,也只能由 AI 来 Review, 到头来就是 AI 与 AI 自己玩,有 Bug 也只有 AI 看得懂。以后的屎山代码是一车一车的来。
除了从 JDK 官方每个版本的 What's New in JDK 18 - New Features and Enhancements, 还可以看 OpenJDK JDK 18 列出的更简明的新特性。自 JDK 10 之后,每一版的新特性由链接 https://openjdk.org/projects/jdk/<version>/ 查看,如 JDK 10 新特性链接为 https://openjdk.org/projects/jdk/10/。
在 https://openjdk.org/projects/jdk/18/ 列出了 JDK 18 如下新特性- 400: UTF-8 by Default
- 408: Simple Web Server
- 413: Code Snippets in Java API Documentation
- 416: Reimplement Core Reflection with Method Handles
- 417: Vector API (Third Incubator)
- 418: Internet-Address Resolution SPI
- 419: Foreign Function & Memory API (Second Incubator)
- 420: Pattern Matching for switch (Second Preview)
- 421: Deprecate Finalization for Removal
找几个有代表性的着重加了学习 Read More
昨天单列了 Java 17 新特性之密封类型, 继续刷 What's New in JDK 17 - New Features and Enhancements.switch 模式匹配(预览)
在 Java 21 才正式放出,主动就是原来的 switch...case 语句可以写成表达式的方式,有返回值, 无需每个分支的 break, 并增加模式匹配功能,比如匹配类型,带约束子条件,匹配 record 的字段值等,这里不展开说明,待到详细了解 Java 21 新特性时再深入研究。新的 macOS 渲染管道
英文是 New macOS Rendering Pipeline, 主要是 Swing API 如果配置系统属性-Dsun.java2d.meta=true就可以用 Apple Metal 替代 OpenGL 加速渲染界面,很少写 Java Swing 桌面应用的略过。新的 API 可访问大图标
同样是在对 Swing 的改进,可用 FileSystemView 加载解析度的图标 Read More
工作中所有项目都已升级到了当前的 LTS 版 Java 21, 眼看 Java 快要来到了下一个 LTS 版本 - Java 25,将于今年 9 月份发布。四年前记录过一篇 Java 10 ~ 16 一路向前冲(新特性一箩筐),其中夹杂着孵化,预览中的以及正式的特性。现在继续跟随着 Java 16 之后版本的特性,主要讲述正式的,可直观体验到的特性,孵化与预览中的特性只会简单提及。
先还是看下 Java 的发布日期安排 Oracle Java SE Support Roadmap版本 发布日 原定支持至 延期支持至
也就是每两年(9月份)会有一个 LTS 版本,中间若干过度版本。在正式项目中尽可能只用 LTS 版本,因为 LTS 版更稳定,且有长期的补丁,不能项目进行中不得不在进行非 LTS 主版本升级。 Read More
Java 17(LTS) 2021/9 2026/9 2029/12
Java 18-20 2022/3 - 2023/3 2022/9 - 2023/9 N/A
Java 21(LTS) 2023/9 2028/9 2031/9
Java 22 2024/3 2024/9 N/A
Java 23 2024/9 2025/3 N/A
Java 24 2025/3 2025/9 N/A
Java 25(LTS) 2025/9 2030/9 2033/9
使用 Tomcat 时应根据服务器的负载和客户端能接受的等待情可适当的调节 maxThreads, acceptCount, maxConnections 的值。这三个参数只有 maxThreads 是最容易理解,即 Tomcat 当前最大同时处理请求的数目,其他两个参数有些模糊。而搜索网络相关的解释发现一些相互矛盾的地方,本文将通过调整这几个值,实际体验它们对请求连接的影响。
在测试之前,先看看 Tomcat 官网的解释,你可能不信 AI 的胡说八道,官网仍然是最可信的。在关于 The HTTP Connector 一章中,找到它们三者之间的说明原文是Each incoming, non-asynchronous request requires a thread for the duration of that request. If more simultaneous requests are received than can be handled by the currently available request processing threads, additional threads will be created up to the configured maximum (the value of the
用 Google 翻译后 Read MoremaxThreadsattribute). If still more simultaneous requests are received, Tomcat will accept new connections until the current number of connections reachesmaxConnections. Connections are queued inside the server socket created by the Connector until a thread becomes available to process the connection. OncemaxConnectionshas been reached the operating system will queue further connections. The size of the operating system provided connection queue may be controlled by theacceptCountattribute. If the operating system queue fills, further connection requests may be refused or may time out.
早先对 Java ArrayList 的扩容理解是在 new ArrayList() 时会默认建立一个内部容量为 16(这个数值还是错的,往后看) 大小的数组,然而插入数据容量不足时会扩容为原来的 1.5 倍,并用 System.arraycopy() 移动原来的数组到新的大数组中,所以为了频繁的内部扩容操作,在已知 ArrayList 将来大小的情况下,应该在创建 ArrayList 时指定大小,如 new ArrayList(1000)。那么是否指定初始容量对性能会有多大的影响仍缺乏感性的认识。
本文通过具体的测试主要掌握以下知识- new ArrayList() 默认容量大小(JDK 8 以前是 10, JDK 8 及以后为 0)
- ArrayList 何时进行扩容,以及每次扩容多少
- new ArrayList() 时是否指定初始容量值的性能对比
- 除了 ArrayList 自动扩容外,它会不会自动缩容呢?
new ArrayList() 的默认容量多少及增容策略
就像 JDK 8 的 HashMap 引入了红黑树改善性,随着 JDK 版本的升级 ArrayList 的内部实现也在演进。回到 JDK 7, 当我们不指定容量 new ArrayList() 创建一个对象时的实现是 Read More
Java 代码中如果显式的用throw关键字抛出异常,那么在该分支中其后的语句不可到达,并且即使对于有返回值的函数也不必写return语句了。像下面的代码1private static int foo(int num) { 2 if (num == 0) { 3 throw new RuntimeException(""); 4 } else { 5 return num + 1; 6 } 7}
以上代码是合法的。要清洁代码的话,最后的return num + 1不必写在else条件中,这样写只是为了验证抛出异常后不必有返回值。
比如我们想对该代码进行重构,把throw语句抽取到一个方法中,以便于在该方法中集中处理错误信息,于是变成了 Read More
不得不承认因为 ChatGPT 为代表的 AI 的出现,让许多技术博客的写作者积极性大大降低。但本着以学习掌握知识为目的,实战,写下来对加强学习仍然是非常有意义的。如果一直使用 AI 来解决技术问题,知识永远是 AI 的,至于说有了 AI 本应没有主动学习必要的性的话,永远保持像一张白纸,A4 大小,那真就无话可说了。
开发过程驱动有分 TDD(Test-Driven Development) 和 BDD(Behavior-Driven Development),大致的理解是 TDD 更关注实现细节,BDD 更接近于 QA 的测试,对领域的测试。BDD 从抽象中来讲更适于做面向用户的集成测试。当然在 AI 生成代码的年代可能单玩测试反而不那么重要,因为更多是一次性代码。
BDD 给人最典型的印象是 Scenario/Given/When/Then, BDD 最流行的测试框架当属 Cucumber, 它以插件的方式支持众多编程语方,如官方支持的用 JavaScript, Java, Kotlin, Ruby, Lua, Scala, C++, Go, OCaml, 还有其他半官支持的 Python, Swift/ObjC, Perl, .NET(C#, F#, VB), 以及非官方支持的 Rust, D, Groovy 等。
另外还有一个专供 Java 的轻量级 JBehave, 不过个人更推荐用 Cucumber, 因为 Cucumber 得到更多 IDE 如 IntelliJ, Eclipse, VS Code 等的支持,并能与 JUnit 4, JUnit 5, TestNG, 以及 Spring Boot 项目集成,内置的测试报告插件,多语言当然是个亮点。
本文主要关注 Maven 项目中如何使用 Cucumber, 循序渐进的从简单的测试开始,然后跃进到与 JUnit 5/ JUnit 4 的结合,以及普通 Unit Test 和 BDD 测试如何并存且可区分的执行,或者在 Maven 中创建独立的 src/bdd 目录单独存放 BDD 测试用例。 Read More
回看三年前的一篇日志 Mockito 3.4.0 开始可 Mock 静态方法,最后对 Mockito 产生的缺憾是它无法用来 Mock 非测试线程(主线程)中的静态方法调用。其实这也是可以变通的,下面慢慢道来。
首先回顾一下 Mockito 的静态方法 Mock 的使用方法,随着 Mockito 版本的升级,引入依赖的方式也发生了些许的变化,以 Maven 项目为例,如果在 JUnit 5 下用 Mockito 的 pom.xml 依赖中为1<dependency> 2 <groupId>org.mockito</groupId> 3 <artifactId>mockito-junit-jupiter</artifactId> 4 <version>5.14.1</version> 5 <scope>test</scope> 6</dependency>
由它引入的全部相关依赖
Read More