You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何配置Java 17程序反射访问Java 19虚拟线程API

如何在兼容Java 17的程序中反射调用Java 19虚拟线程API?

我要发布兼容Java 17的程序,同时通过反射检测当前JVM是否支持Thread.ofVirtual().name("abc").factory()来使用Java 19的虚拟线程特性,但Java模块机制限制了反射访问内部API。已经在module-info.java中添加requires java.base,但问题依然存在,求可行的JVM参数或模块配置方案。


JShell测试报错

|  欢迎使用 JShell -- 版本 19.0.2
|  输入 /help intro 查看介绍

jshell> Thread.class.getMethod("ofVirtual")
   ...>                 .invoke(null)
   ...>                 .getClass()
   ...>                 .getMethod("name", String.class, Long.TYPE)
   ...>                 .setAccessible(true)
|  异常 java.lang.reflect.InaccessibleObjectException: 无法使 public java.lang.Thread$Builder$OfVirtual java.lang.ThreadBuilders$VirtualThreadBuilder.name(java.lang.String,long) 可访问: 模块 java.base 未向未命名模块 @30dae81 打开 java.lang
|        在 AccessibleObject.throwInaccessibleObjectException (AccessibleObject.java:387)
|        在 AccessibleObject.checkCanSetAccessible (AccessibleObject.java:363)
|        在 AccessibleObject.checkCanSetAccessible (AccessibleObject.java:311)
|        在 Method.checkCanSetAccessible (Method.java:201)
|        在 Method.setAccessible (Method.java:195)
|        在 (#1:5)

Exception java.lang.reflect.InaccessibleObjectException: 无法使 public java.lang.Thread$Builder$OfVirtual java.lang.ThreadBuilders$VirtualThreadBuilder.name(java.lang.String,long) 可访问: 模块 java.base 未向未命名模块 @30dae81 打开 java.lang


模块配置文件(添加requires java.base后)

// src/main/java/module-info.java
module test_20230518_ {
    requires java.base;
}

测试代码

// src/main/java/a/A.java

package a;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.ThreadFactory;

public class A {
    public static void main(String[] args) {
        ThreadFactory threadFactory = tf();
        threadFactory.newThread(() ->
                System.out.println("hi from " +
                        Thread.currentThread().getName()));
    }

    private static ThreadFactory tf() {
        Method[] methods = Thread.class.getMethods();
        boolean haveVirtual = Arrays.stream(methods)
                .anyMatch(m -> m.getName().equals("ofVirtual") &&
                        m.getParameterCount() == 0);

        if (haveVirtual) {
            try {
                Object b = Thread.class.getMethod("ofVirtual")
                        .invoke(null);
                b = b.getClass().getMethod("name", String.class, Long.TYPE)
                        .invoke(b, "prefix-", (long) 1);
                b = b.getClass().getMethod("factory")
                        .invoke(b);
                return (ThreadFactory) b;
            } catch (Throwable t) {
                throw new RuntimeException(t);
            }
        } else {
            return Thread::new;
        }
    }
}

运行报错

Exception in thread "main" java.lang.RuntimeException: java.lang.IllegalAccessException: 类 a.A 无法访问类 java.lang.ThreadBuilders$VirtualThreadBuilder(位于模块 java.base 中)的带有修饰符 "public volatile" 的成员
    at a.A.tf(A.java:31)
    at a.A.main(A.java:9)
Caused by: java.lang.IllegalAccessException: 类 a.A 无法访问类 java.lang.ThreadBuilders$VirtualThreadBuilder(位于模块 java.base 中)的带有修饰符 "public volatile" 的成员
    at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:420)
    at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:709)
    at java.base/java.lang.reflect.Method.invoke(Method.java:569)
    at a.A.tf(A.java:26)
    ... 1 more

class a.A 无法访问类 java.lang.ThreadBuilders$VirtualThreadBuilder(位于模块 java.base 中)的带有修饰符 "public volatile" 的成员


解决方案

方案1:通过JVM参数开放模块访问

虚拟线程的ThreadBuilders$VirtualThreadBuilder属于java.base模块的java.lang包,需要通过JVM参数让模块向你的程序开放反射权限:

  • 如果程序是模块化的(有module-info.java),添加参数:
    --add-opens java.base/java.lang=test_20230518_
    
    其中test_20230518_是你定义的模块名。
  • 如果程序是非模块化的(运行时在未命名模块),添加参数:
    --add-opens java.base/java.lang=ALL-UNNAMED
    

另外,Java 19的虚拟线程是预览特性,运行时必须添加:

--enable-preview

完整运行命令(模块化程序示例):

java --enable-preview --add-opens java.base/java.lang=test_20230518_ -cp <你的类路径> a.A

方案2:优化反射逻辑,避免访问内部类

问题核心是你直接反射调用了内部实现类ThreadBuilders$VirtualThreadBuilder的方法,改成操作公开的Thread.Builder.OfVirtual接口即可绕过模块限制,不需要额外JVM参数:

修改tf()方法如下:

private static ThreadFactory tf() {
    Method[] methods = Thread.class.getMethods();
    boolean haveVirtual = Arrays.stream(methods)
            .anyMatch(m -> m.getName().equals("ofVirtual") &&
                    m.getParameterCount() == 0);

    if (haveVirtual) {
        try {
            // 获取公开的Thread.ofVirtual()方法,返回Thread.Builder.OfVirtual接口实例
            Method ofVirtualMethod = Thread.class.getMethod("ofVirtual");
            Object builder = ofVirtualMethod.invoke(null);
            
            // 通过公开接口类获取name方法
            Class<?> builderInterface = Class.forName("java.lang.Thread$Builder$OfVirtual");
            Method nameMethod = builderInterface.getMethod("name", String.class, long.class);
            builder = nameMethod.invoke(builder, "prefix-", 1L);
            
            // 通过公开接口类获取factory方法
            Method factoryMethod = builderInterface.getMethod("factory");
            Object factory = factoryMethod.invoke(builder);
            
            return (ThreadFactory) factory;
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    } else {
        return Thread::new;
    }
}

注意事项

  1. Java 19/20的虚拟线程是预览特性,Java 21才正式成为标准特性。兼容Java 17时,必须通过反射检测API是否存在,避免低版本JVM直接加载相关类导致ClassNotFoundException。
  2. --add-opens属于打破模块封装的非常规手段,未来JDK版本可能调整,优先推荐方案2的反射逻辑优化。

内容的提问来源于stack exchange,提问作者Dave Ankin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 19:34:58