从新的 JNI 线程通知 Java

Notify Java from new JNI thread

我制作了一个 Java 调用 JNI 的程序,在 JNI 中它创建了一个“JNICallback”线程并在其中执行 env-attachCurrentThreadAsDaemon()

我的目标是从另一个纯 Java 线程 JNICallback 线程通知。 我做一些标准的事情,比如 synchronized -> wait, synchronized -> notifyAll,使用 static final Object 作为同步。 如果我 运行 它在 Eclipse 下,一切正常。

但是如果我将它导出为“运行nable jar 文件”和 运行 在同一个 JVM 下,我看到我的 JNI 程序调用 monitorEnter, notifyAll, monitorExit 没有错误,但是 Java 没有通知。 在调试器中,我看到 JNI 和 Java 使用的同一个对象有不同的 ObjectId,这对我来说很可疑(它是下面代码中的名称 onConnectEvent) 我什至将通知代码从 JNI 移动到 Java,并使用 JNI 的静态无效方法调用,在 Java 中执行同步->notifyAll,但这只告诉我等待线程在不同的 ObjectId 上等待,所以估计是这个原因。

...
    static final Object onConnectEvent = new Object();
...
    Thread dogServerConnect,...;
...
    public MainClass(){
        dogServerConnect = runDog(new ServerConnectionWatchDog(), "onServerConnect");
        someJNIstuffPlusNewThreadsCreation();
...
    
    static void onJNICallbackEvent(int type) {
        switch (type) {
        case 1 -> {
            synchronized(onConnectEvent) {
                onConnectEvent.notifyAll();
                 ^^^^^ got to this line from JNI, just fine
            }
...
    class ServerConnectionWatchDog implements Runnable {
        @Override
        public void run() {
            while(true)
                synchronized(onConnectEvent) {
                    try {
                        onConnectEvent.wait();
                        for (var l : listeners) {
^^^^^^^^^^^^^^^^^^^^^^^^^ never come to this line
...

同样,这在 Eclipse 下确实运行良好,但由于某些原因作为单独的 jar 文件失败。 为什么同一个 final 静态对象对于不同的线程可能有不同的实例 id,或者现在这样就好了?

我的意思是对于等待线程,Eclipse 向我显示“正在等待:对象 (id=48)”,对于在“notifyAll”执行之前的本机线程,它向我显示“拥有:对象 (id=50)”。也许这是因为我手动暂停了等待线程,以查看对象 ID(然后 运行 再次出现,这不是原因) 我能错过什么?

在 Android 的常见问题解答中找到了答案:why didn't FindClass find my class。 虽然我的环境不是Android,但还是有道理的。 事情是 env->FindClass 展开 Java 堆栈以找到调用者 class 并使用它的 classloader。 对于在 JNI 中启动的本机线程,Java 堆栈是空的。因此,这给了我们唯一的选择,要么在创建本机线程之前获取对所需 classes 的全局引用,要么在同一位置获取对其 classloader 的引用,然后使用 FindClass 来自保存了 classLoader,比较难。 此解决方案可防止 Eclipse JarRsrcLoader 创建重复的静态对象。 可能有人会说在 JNI 中新创建的本机线程中执行 env->FindClass 是一个糟糕的设计,至少要谨慎使用。