从 JNI 更改 C++ 中定义的数组的问题

Issues with changing array defined in C++ from JNI

我在 Java 中编写了一个使用 JNI 调用 C++ 代码的本机方法。

我的 JNI 工作正常,但是当我用 JNI 修改一个用 C++ 创建的数组时,它只显示方法内部的修改,而不显示方法外部的修改。

Java class:

package test;

public class JavaTest {

    static {
        System.loadLibrary("native");
    }
    
    private native void start();
    
    private native void updateArray(int index, boolean result);
    
    private void runStuff() throws Exception {
        new Thread(() -> start()).start();
        
        while (true) {
            updateArray(0, true);
            Thread.sleep(1000);
            updateArray(0, false);
        }
    }
    
    public static void main(String[] args) throws Exception {
        new JavaTest().runStuff();
    }
    
}

while循环正在调用C++函数来改变数组,这是我可以看到函数体内变化的地方。

C++代码:

#include "test_JavaTest.h"
#include <iostream>

#ifdef _WIN32
#include <Windows.h>
#else
#include <unistd.h>
#endif

bool arr[3] = {false, false, false};

JNIEXPORT void JNICALL Java_test_JavaTest_start(JNIEnv *, jobject) {
    while (true) {
        std::cout << "value: " << arr[0] << std::endl;
        Sleep(1000);
    }
}

JNIEXPORT void JNICALL Java_test_JavaTest_updateArray(JNIEnv * env, jobject thisObject, jint index, jboolean result) {
    arr[index] = result;
    std::cout << "local value: " << arr[index] << std::endl;
}

输出:

value: 1
value: 1
local value: 0
local value: 1
value: 1
local value: 0
local value: 1
value: 1
local value: 0
local value: 1
value: 1
local value: 0
local value: 1
value: local value: 10
local value: 1
value: 1
local value: 0
local value: 1
value: 1
local value: 0
local value: 1

“值”打印语句不会改变,但“本地值”打印语句会改变。

考虑一下这个循环中会发生什么:

    while (true) {
        updateArray(0, true);
        Thread.sleep(1000);
        updateArray(0, false);
    }

如果我们在心里“展开”它,问题就会变得很明显:

        updateArray(0, true);
        Thread.sleep(1000);
        updateArray(0, false);
        updateArray(0, true);
        Thread.sleep(1000);
        updateArray(0, false);
        updateArray(0, true);
        Thread.sleep(1000);
        updateArray(0, false);

另一个线程根本看不到值 false,因为它立即变回 true

如果在updateArray(0, false);之后再添加一个Thread.sleep(1000);,效果会更好:

    while (true) {
        updateArray(0, true);
        Thread.sleep(1000);
        updateArray(0, false);
        Thread.sleep(1000);
    }

但是,您应该在线程之间使用某种同步。不能保证 Sleep 或 JNI 调用充当同步机制,并且在没有同步的情况下读写内存将调用未定义的行为(在这种特定情况下,它可能看起来像预期的那样工作,因为编译器不能“请参阅“过去 Sleep,并且可能会通过优化数组访问来确保安全,尤其是考虑到它的外部链接。