首页 > 解决方案 > 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 函数所固有的,还是我遗漏了什么?

标签: javamultithreadingjava-native-interfacethread-safetyopenmp

解决方案


一种可能的解决方案是在访问 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);
}

推荐阅读