从 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
,并且可能会通过优化数组访问来确保安全,尤其是考虑到它的外部链接。
我在 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
,并且可能会通过优化数组访问来确保安全,尤其是考虑到它的外部链接。