JNI新建线程通知Java线程在Eclipse正常、导出可运行Jar后失效问题咨询
问题根因分析
static final修饰的变量是类加载器级别的单例,不是JVM全局单例。你遇到的同个静态变量有两个不同ObjectID的情况,本质是你的MainClass被两个不同的类加载器分别加载了,两套静态变量互相独立,用各自的对象做锁自然无法实现等待通知逻辑。
Eclipse中运行正常是因为Eclipse默认用同一个应用类加载器加载所有项目类,不会出现重复加载的情况;导出可运行Jar后出现问题,通常是Jar打包方式、JNI类查找逻辑两个方向出了问题。
排查点清单
- 验证类加载器是否一致
分别在ServerConnectionWatchDog.run()和onJNICallbackEvent()方法中添加日志,打印当前MainClass对应的类加载器:
运行Jar后如果两次打印结果不一致,即可确认是类重复加载问题。System.out.println(MainClass.class.getClassLoader()); - 检查JNI侧类查找逻辑
你通过env->FindClass()查找MainClass的操作如果是在attach的JNICallback守护线程中执行的,会出现类加载器异常:原生线程attach到JVM后没有默认的上下文类加载器,FindClass只会从启动类加载器路径搜索类,大概率会加载到错误的MainClass实例。
解决方法:在Java侧启动时就把onConnectEvent对象的全局引用缓存到JNI侧,后续直接用这个缓存的对象做锁操作,不要在原生线程中动态查找类和静态变量。 - 检查可运行Jar的打包配置
如果你导出Jar时选择了「将依赖库打包到Jar中」的选项,要检查解压后的Jar内是否存在重复的MainClass字节码文件,或者依赖包中是否存在同包同名的类。 - 快速验证锁对象冲突
你可以临时把锁对象替换为全局唯一的字符串常量,字符串常量不受类加载器影响,全局共享同个实例:
如果替换后通知逻辑恢复正常,即可完全确认是类加载器导致的静态变量多实例问题。static final Object onConnectEvent = "MY_UNIQUE_LOCK_KEY_2024";
内容的提问来源于stack exchange,提问作者Павел
相关产品推荐
相关产品推荐

