java - JNI 函数是线程安全的吗?
问题描述
我正在尝试使用 OpenMP 来并行化将 Java 字符串数组转换为 C 字符串数组(char**)的过程:
char** convert(JNIEnv *env, jobjectArray jstringArray, int n_threads){
int n_rows =(*env)->GetArrayLength(env, jstringArray);
char **cArray = (char **) malloc(n_rows * sizeof(char*));
int i=0;
jstring row;
if(n_threads <= 0 ) n_threads = 1;
#pragma omp parallel for num_threads(n_threads) private(i, row)
for (i=0; i<n_rows; i++) {
row = (jstring) (*env)->GetObjectArrayElement(env, jstringArray, i);
cArray[i] = (char*)(*env)->GetStringUTFChars(env, row, 0);
printf("cArray[%d]: %s thread:%d row:%p env:%p jstringArray:%p\n", i, cArray[i], omp_get_thread_num(), row, env, jstringArray);
}
return cArray;
}
但是,在获取 jstring 行时,我似乎遇到了竞争情况:
n_threads = 1 的输出:
cArray[0]: AA thread:0 row:0x7f5b1c000c90 env:0x7f5b300091f8 jstringArray:0x7f5bb3ffdc38
cArray[1]: BB thread:0 row:0x7f5b1c000c98 env:0x7f5b300091f8 jstringArray:0x7f5bb3ffdc38
cArray[2]: CC thread:0 row:0x7f5b1c000ca0 env:0x7f5b300091f8 jstringArray:0x7f5bb3ffdc38
cArray[3]: DD thread:0 row:0x7f5b1c000ca8 env:0x7f5b300091f8 jstringArray:0x7f5bb3ffdc38
n_threads = 3 的输出:
cArray[0]: AA thread:0 row:0x7f434c004050 env:0x7f434800a9f8 jstringArray:0x7f435b8f0c48
cArray[1]: BB thread:0 row:0x7f434c004060 env:0x7f434800a9f8 jstringArray:0x7f435b8f0c48
cArray[2]: **CC** thread:1 **row:0x7f434c004058** env:0x7f434800a9f8 jstringArray:0x7f435b8f0c48
cArray[3]: **CC** thread:2 **row:0x7f434c004058** env:0x7f434800a9f8 jstringArray:0x7f435b8f0c48
冲突的函数似乎是 GetObjectArrayElement(),它为两个不同的线程(示例中的 1 和 2)和 2 个不同的数组索引(2 和 3)返回相同的引用。
这种行为是所述 JNI 函数所固有的,还是我遗漏了什么?
解决方案
一种可能的解决方案是在访问 JNI 函数之前将每个线程附加到 JVM,如下所示:
JavaVM* jvm = NULL;
JNIEnv *t_env;
(*env)->GetJavaVM(env, &jvm);
#pragma omp parallel num_threads(n_threads) private(t_env)
{
(*jvm)->AttachCurrentThread(jvm, (void**)&t_env, NULL);
// Call JNI functions here using t_env...
(*jvm)->DetachCurrentThread(jvm);
}