JNI创建线程调用Clojure Java互操作时如何避免ClassNotFoundException
问题根因
该报错并非clojure.lang.RT类实际缺失,核心原因是JNI后台挂载线程的上下文类加载器配置错误:
- 通过
AttachCurrentThread挂载的原生后台线程,默认使用JVM系统启动类加载器,该加载器没有权限加载Clojure核心类及项目自定义类 - 无类型提示的
alength会触发Clojure动态反射逻辑,需要通过当前线程的上下文类加载器加载clojure.lang.RT完成调用,因此抛出类找不到异常;添加类型提示后alength会被编译为直接调用JDK原生数组长度获取逻辑,无需依赖RT类,因此不会报错 - Clojure的
future创建的线程会自动继承父线程的上下文类加载器,因此调用逻辑正常,无法复现问题
解决方案(无需添加类型提示)
方案1:JNI挂载线程时主动设置上下文类加载器(推荐)
在原生侧完成线程挂载后,主动配置正确的上下文类加载器即可解决问题,步骤如下:
- 在Java/Clojure侧的正常工作线程(可正常调用Clojure函数的主线程)中,提前缓存当前线程的上下文类加载器实例,暴露给原生侧调用
- 原生侧调用
AttachCurrentThread完成挂载后,主动为当前线程设置缓存的类加载器
原生侧C代码示例:
// 获取当前线程实例 jclass thread_cls = (*env)->FindClass(env, "java/lang/Thread"); jmethodID current_thread_mid = (*env)->GetStaticMethodID(env, thread_cls, "currentThread", "()Ljava/lang/Thread;"); jobject current_thread = (*env)->CallStaticObjectMethod(env, thread_cls, current_thread_mid); // 为当前线程设置提前缓存的正确类加载器 jmethodID set_cl_mid = (*env)->GetMethodID(env, thread_cls, "setContextClassLoader", "(Ljava/lang/ClassLoader;)V"); (*env)->CallVoidMethod(env, current_thread, set_cl_mid, cached_class_loader);
方案2:全局开启Clojure直接链接编译
如果不想修改原生侧代码,可在项目编译配置中开启直接链接,让Clojure编译器尽可能将函数调用编译为直接调用,无需通过RT类走反射逻辑:
;; Leiningen项目在project.clj中添加配置 :global-vars {*compiler-options* {:direct-linking true}}
;; deps.edn项目添加配置 :jvm-opts ["-Dclojure.compiler.direct-linking=true"]
内容的提问来源于stack exchange,提问作者Eric D
相关产品推荐
相关产品推荐

