如何在Java 21中利用虚拟线程优化Oracle数据库的异步调用

陌瑶同学_8288

陌瑶同学_8288

2026-06-08

743人浏览

原创

java 21虚拟线程不支持oracle jdbc原生异步i/o,但可通过同步阻塞调用+虚拟线程调度提升高并发查询吞吐量,需驱动≥23.7且保持useasyncio=false默认值。

直接上结论:java 21 虚拟线程本身不支持 oracle jdbc 驱动的原生异步 i/o,但你可以用同步阻塞调用 + 虚拟线程调度的方式,在不改业务逻辑的前提下显著提升 oracle 数据库高并发查询吞吐量——前提是驱动版本 ≥ 23.7 且启用 oracle.jdbc.useasyncio=false(默认就是 false)。

为什么不能直接用 CompletableFuture + Oracle JDBC 做“真异步”

Oracle 官方 JDBC 驱动(ojdbc11 及更早版本)至今未实现 java.nio.channels.AsynchronousChannel 接口,所有 Connection.createStatement()、PreparedStatement.execute() 等调用本质都是同步阻塞的。强行套 CompletableFuture.supplyAsync() 只是把阻塞扔进平台线程池,反而加剧线程竞争和上下文切换开销。

常见错误现象:
- 使用 Executors.newFixedThreadPool(50) 提交 1000 个 Oracle 查询,大量任务排队等待线程空闲
- jstack 显示大量线程卡在 java.net.SocketInputStream.socketRead0 或 oracle.jdbc.driver.T4CConnection.logon
- 吞吐量卡在 300–500 QPS,CPU 利用率却只有 40%,大量时间花在空等网络响应上

正确做法:用虚拟线程包裹阻塞 JDBC 调用

虚拟线程的价值恰恰体现在这种“已知会阻塞、但又不想写回调”的场景。JVM 在遇到 Socket.read()、Thread.sleep()、BlockingQueue.take() 等可卸载点时,会自动将当前虚拟线程挂起,并释放底层载体线程(Carrier Thread)去执行其他任务。

实操建议:

  • 确保 JDK 版本为 21.0.2+ 或更高(推荐 21.0.4,修复了早期虚拟线程与 TLS 握手的竞态问题)
  • 使用 Executors.newVirtualThreadPerTaskExecutor(),不要复用自定义线程池
  • Oracle 连接 URL 中显式关闭连接池自动提交(避免隐式事务干扰):jdbc:oracle:thin:@host:1521:orcl?useServerPrepStmts=true&rewriteBatchedStatements=true
  • 每个查询封装为独立 Callable<resultset></resultset>,由虚拟线程执行,不要在虚拟线程内共享 Connection 实例(连接不是线程安全的)

示例代码片段:

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    List<future>>> futures = new ArrayList();
    for (int i = 0; i  {
            try (Connection conn = dataSource.getConnection();
                 PreparedStatement ps = conn.prepareStatement("SELECT name FROM users WHERE id = ?")) {
                ps.setInt(1, id);
                try (ResultSet rs = ps.executeQuery()) {
                    List<string> result = new ArrayList();
                    while (rs.next()) result.add(rs.getString("name"));
                    return result;
                }
            }
        }));
    }
    // 批量收集结果(注意:此处不阻塞主线程)
    futures.forEach(future -> {
        try {
            System.out.println(future.get()); // get() 仍会阻塞,但只阻塞当前虚拟线程
        } catch (Exception e) {
            e.printStackTrace();
        }
    });
}</string></future>

必须避开的三个 Pinning 陷阱

一旦虚拟线程被“Pin”住(即无法挂起),它就会长期独占一个载体线程,失去 M:N 调度优势,退化为传统线程行为。Oracle 场景中最容易触发 Pinning 的操作:

  • synchronized 块包裹数据库连接获取或结果处理逻辑 —— 改用 ReentrantLock 或直接去掉(多数场景无需同步)
  • 在虚拟线程中调用 JNI 封装的 Oracle 加密函数(如 oracle.security.crypto.core.RSA)—— 检查是否启用了 FIPS 模式,禁用或改用 JVM 原生 javax.crypto
  • 使用旧版 Druid 连接池(≤ 1.2.18),其内部 DruidAbstractDataSource 的 getConnectionDirect() 方法含 synchronized —— 升级到 1.2.23+ 或改用 HikariCP(已适配虚拟线程)

验证是否发生 Pinning:
运行时添加 JVM 参数 -XX:+UnlockDiagnosticVMOptions -XX:+PrintVirtualThreadPinned,出现日志如 Pinned at java.net.SocketInputStream.socketRead0(SocketInputStream.java:123) 即表示该调用虽阻塞但未被 Pin,属正常;若出现 Pinned at java.lang.Object.wait(Native Method) 则说明有锁竞争导致 Pin,需排查。

连接池配置与载体线程数的实际影响

虚拟线程不解决连接池瓶颈。即使你启动了 10 万个虚拟线程,如果 Oracle 连接池最大连接数设为 20,所有虚拟线程仍会在 dataSource.getConnection() 处排队等待连接释放——此时瓶颈从线程调度转移到了连接资源。

关键参数建议:

  • HikariCP:maximumPoolSize=200(根据 Oracle 实例 processes 参数留余量),connection-timeout=3000(避免虚拟线程长时间卡在获取连接)
  • 载体线程数默认等于 CPU 核心数,但 Oracle IO 密集型负载下,可适当提高:-Djdk.virtualThread carrierThreads=64(仅当观察到 ForkJoinPool.commonPool().getActiveThreadCount() 长期接近上限时才需调整)
  • 禁用连接测试语句(connection-test-query=NONE),避免每次 getConnection 都触发一次 round-trip

真正容易被忽略的是:虚拟线程不会让单条 SQL 变快,它只是让“等待 SQL 返回”这件事不再浪费操作系统线程。如果你的 SQL 本身执行慢(全表扫描、缺少索引),加再多虚拟线程也无济于事——先用 EXPLAIN PLAN 优化语句,再用虚拟线程放大效果。

Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

oracle java oracle数据库

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

2023.06.15

10677

6

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

2023.07.05

7802

9

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

2023.07.31

6792

8

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.01

1144

3

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.02

948

3

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

1396

5

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

2809

5

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

2023.08.03

20111

3

配置java环境变量
配置java环境变量

配置Java环境变量是为了让操作系统能够识别和使用Java的相关命令和功能。本专题为大家提供配置java环境变量相关文章,帮助大家解决问题。

2023.08.03

1235

8

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程